mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-08-31 01:35:56 +08:00
fix(esxi-agent): pve agent (#24768)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
FROM registry.cn-beijing.aliyuncs.com/yunionio/host-deployer-base:1.5.9
|
||||
FROM registry.cn-beijing.aliyuncs.com/yunionio/host-deployer-base:1.5.11
|
||||
|
||||
LABEL maintainer="Yaoqi Wan wanyaoqi@yunionyun.com"
|
||||
|
||||
|
||||
@@ -108,12 +108,12 @@ require (
|
||||
k8s.io/cri-api v0.28.15
|
||||
k8s.io/klog/v2 v2.90.1
|
||||
moul.io/http2curl/v2 v2.3.0
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260416100244-303b3c130516
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260506084929-0e885263a936
|
||||
yunion.io/x/executor v0.0.0-20260312022053-f538abd2b005
|
||||
yunion.io/x/jsonutils v1.0.1-0.20250507052344-1abcf4f443b1
|
||||
yunion.io/x/log v1.0.1-0.20240305175729-7cf2d6cd5a91
|
||||
yunion.io/x/ovsdb v0.0.0-20230306173834-f164f413a900
|
||||
yunion.io/x/pkg v1.10.4-0.20260224071200-5c269fa62429
|
||||
yunion.io/x/pkg v1.10.4-0.20260422030155-01b100134978
|
||||
yunion.io/x/s3cli v0.0.0-20241221171442-1c11599d28e1
|
||||
yunion.io/x/sqlchemy v1.1.3-0.20251231025938-b0a38f6e9fab
|
||||
yunion.io/x/structarg v0.0.0-20231017124457-df4d5009457c
|
||||
|
||||
@@ -1785,8 +1785,8 @@ sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260416100244-303b3c130516 h1:Fq+hzBZMrIuPP+AX8x8ffQr2Kk3v2UHvsGl9wtG4Lic=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260416100244-303b3c130516/go.mod h1:TbMuTWxcTdyL2Usn+WQdIw0jdlBYO66SzPRY8LZ3Wj0=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260506084929-0e885263a936 h1:93yJirDxKfSvliBCHfwQgc/fHS33qC+YHnbhj/lXFww=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260506084929-0e885263a936/go.mod h1:TbMuTWxcTdyL2Usn+WQdIw0jdlBYO66SzPRY8LZ3Wj0=
|
||||
yunion.io/x/executor v0.0.0-20260312022053-f538abd2b005 h1:3sWwcjGXGjG9mLBWa7AyLq+QSi0udTAx21pfVQRFMBE=
|
||||
yunion.io/x/executor v0.0.0-20260312022053-f538abd2b005/go.mod h1:Uxuou9WQIeJXNpy7t2fPLL0BYLvLiMvGQwY7Qc6aSws=
|
||||
yunion.io/x/jsonutils v0.0.0-20190625054549-a964e1e8a051/go.mod h1:4N0/RVzsYL3kH3WE/H1BjUQdFiWu50JGCFQuuy+Z634=
|
||||
@@ -1800,8 +1800,8 @@ yunion.io/x/ovsdb v0.0.0-20230306173834-f164f413a900 h1:Hu/4ERvoWaN6aiFs4h4/yvVB
|
||||
yunion.io/x/ovsdb v0.0.0-20230306173834-f164f413a900/go.mod h1:0vLkNEhlmA64HViPBAnSTUMrx5QP1CLsxXmxDKQ80tc=
|
||||
yunion.io/x/pkg v0.0.0-20190620104149-945c25821dbf/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
|
||||
yunion.io/x/pkg v0.0.0-20190628082551-f4033ba2ea30/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
|
||||
yunion.io/x/pkg v1.10.4-0.20260224071200-5c269fa62429 h1:yskc2HFnBZXEzfYeQBFdL2gXzVfdPQ8yT4qVDyuhajM=
|
||||
yunion.io/x/pkg v1.10.4-0.20260224071200-5c269fa62429/go.mod h1:0Bwxqd9MA3ACi119/l02FprY/o9gHahmYC2bsSbnVpM=
|
||||
yunion.io/x/pkg v1.10.4-0.20260422030155-01b100134978 h1:+BS9DValwi0xHa92xrB+RR6Cxp7bDbXVysa/KiC18rY=
|
||||
yunion.io/x/pkg v1.10.4-0.20260422030155-01b100134978/go.mod h1:0Bwxqd9MA3ACi119/l02FprY/o9gHahmYC2bsSbnVpM=
|
||||
yunion.io/x/s3cli v0.0.0-20241221171442-1c11599d28e1 h1:1KJ3YYinydPHpDEQRXdr/T8SYcKZ5Er+m489H+PnaQ4=
|
||||
yunion.io/x/s3cli v0.0.0-20241221171442-1c11599d28e1/go.mod h1:0iFKpOs1y4lbCxeOmq3Xx/0AcQoewVPwj62eRluioEo=
|
||||
yunion.io/x/sqlchemy v1.1.3-0.20251231025938-b0a38f6e9fab h1:5m/bSzW3uTAk83rp9eethbYsxJFYInFVeU1RDkteW4E=
|
||||
|
||||
@@ -70,33 +70,35 @@ type GuestdiskUpdateInput struct {
|
||||
}
|
||||
|
||||
type GuestdiskJsonDesc struct {
|
||||
DiskId string `json:"disk_id"`
|
||||
Driver string `json:"driver"`
|
||||
CacheMode string `json:"cache_mode"`
|
||||
AioMode string `json:"aio_mode"`
|
||||
Iops int `json:"iops"`
|
||||
Throughput int `json:"throughput"`
|
||||
Bps int `json:"bps"`
|
||||
Size int `json:"size"`
|
||||
TemplateId string `json:"template_id"`
|
||||
ImagePath string `json:"image_path"`
|
||||
StorageId string `json:"storage_id"`
|
||||
StorageType string `json:"storage_type"`
|
||||
Migrating bool `json:"migrating"`
|
||||
Path string `json:"path"`
|
||||
Format string `json:"format"`
|
||||
Index int8 `json:"index"`
|
||||
BootIndex *int8 `json:"boot_index"`
|
||||
MergeSnapshot bool `json:"merge_snapshot"`
|
||||
Fs string `json:"fs"`
|
||||
Mountpoint string `json:"mountpoint"`
|
||||
Dev string `json:"dev"`
|
||||
IsSSD bool `json:"is_ssd"`
|
||||
NumQueues uint8 `json:"num_queues"`
|
||||
AutoReset bool `json:"auto_reset"`
|
||||
PCIPath string `json:"pci_path"`
|
||||
DiskId string `json:"disk_id"`
|
||||
Driver string `json:"driver"`
|
||||
CacheMode string `json:"cache_mode"`
|
||||
AioMode string `json:"aio_mode"`
|
||||
Iops int `json:"iops"`
|
||||
Throughput int `json:"throughput"`
|
||||
Bps int `json:"bps"`
|
||||
Size int `json:"size"`
|
||||
TemplateId string `json:"template_id"`
|
||||
ImagePath string `json:"image_path"`
|
||||
StorageId string `json:"storage_id"`
|
||||
StoragecacheId string `json:"storagecache_id"`
|
||||
StorageExternalId string `json:"storage_external_id"`
|
||||
StorageType string `json:"storage_type"`
|
||||
Migrating bool `json:"migrating"`
|
||||
Path string `json:"path"`
|
||||
Format string `json:"format"`
|
||||
Index int8 `json:"index"`
|
||||
BootIndex *int8 `json:"boot_index"`
|
||||
MergeSnapshot bool `json:"merge_snapshot"`
|
||||
Fs string `json:"fs"`
|
||||
Mountpoint string `json:"mountpoint"`
|
||||
Dev string `json:"dev"`
|
||||
IsSSD bool `json:"is_ssd"`
|
||||
NumQueues uint8 `json:"num_queues"`
|
||||
AutoReset bool `json:"auto_reset"`
|
||||
PCIPath string `json:"pci_path"`
|
||||
|
||||
// esxi
|
||||
// esxi, proxmox
|
||||
ImageInfo struct {
|
||||
ImageType string `json:"image_type"`
|
||||
ImageExternalId string `json:"image_external_id"`
|
||||
|
||||
+19
-18
@@ -927,24 +927,25 @@ type ServerUpdateInput struct {
|
||||
}
|
||||
|
||||
type GuestJsonDesc struct {
|
||||
Name string `json:"name"`
|
||||
Hostname string `json:"hostname"`
|
||||
Description string `json:"description"`
|
||||
UUID string `json:"uuid"`
|
||||
Mem int `json:"mem"`
|
||||
CpuSockets int `json:"cpu_sockets"`
|
||||
Cpu int `json:"cpu"`
|
||||
Vga string `json:"vga"`
|
||||
Vdi string `json:"vdi"`
|
||||
Machine string `json:"machine"`
|
||||
Bios string `json:"bios"`
|
||||
BootOrder string `json:"boot_order"`
|
||||
SrcIpCheck bool `json:"src_ip_check"`
|
||||
SrcMacCheck bool `json:"src_mac_check"`
|
||||
IsMaster *bool `json:"is_master"`
|
||||
IsSlave *bool `json:"is_slave"`
|
||||
IsVolatileHost bool `json:"is_volatile_host"`
|
||||
HostId string `json:"host_id"`
|
||||
Name string `json:"name"`
|
||||
Hostname string `json:"hostname"`
|
||||
Description string `json:"description"`
|
||||
UUID string `json:"uuid"`
|
||||
Mem int `json:"mem"`
|
||||
CpuSockets int `json:"cpu_sockets"`
|
||||
Cpu int `json:"cpu"`
|
||||
Vga string `json:"vga"`
|
||||
Vdi string `json:"vdi"`
|
||||
Machine string `json:"machine"`
|
||||
Bios string `json:"bios"`
|
||||
BootOrder string `json:"boot_order"`
|
||||
SrcIpCheck bool `json:"src_ip_check"`
|
||||
SrcMacCheck bool `json:"src_mac_check"`
|
||||
IsMaster *bool `json:"is_master"`
|
||||
IsSlave *bool `json:"is_slave"`
|
||||
IsVolatileHost bool `json:"is_volatile_host"`
|
||||
ExternalImageId string `json:"external_image_id"`
|
||||
HostId string `json:"host_id"`
|
||||
// 宿主机管理IP
|
||||
HostAccessIp string `json:"host_access_ip"`
|
||||
// 宿主机公网IP(如果有)
|
||||
|
||||
@@ -19,7 +19,12 @@ import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/cloudmux/pkg/cloudprovider"
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/gotypes"
|
||||
"yunion.io/x/pkg/util/cloudinit"
|
||||
"yunion.io/x/pkg/util/httputils"
|
||||
"yunion.io/x/pkg/util/rbacscope"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
@@ -50,7 +55,7 @@ func (self *SProxmoxGuestDriver) GetHypervisor() string {
|
||||
}
|
||||
|
||||
func (self *SProxmoxGuestDriver) GetProvider() string {
|
||||
return api.CLOUD_PROVIDER_PROXMOX
|
||||
return api.CLOUD_PROVIDER_ONECLOUD
|
||||
}
|
||||
|
||||
func (self *SProxmoxGuestDriver) GetMinimalSysDiskSizeGb() int {
|
||||
@@ -77,7 +82,7 @@ func (self *SProxmoxGuestDriver) GetInstanceCapability() cloudprovider.SInstance
|
||||
func (self *SProxmoxGuestDriver) GetComputeQuotaKeys(scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, brand string) models.SComputeResourceKeys {
|
||||
keys := models.SComputeResourceKeys{}
|
||||
keys.SBaseProjectQuotaKeys = quotas.OwnerIdProjectQuotaKeys(scope, ownerId)
|
||||
keys.CloudEnv = api.CLOUD_ENV_PRIVATE_CLOUD
|
||||
keys.CloudEnv = api.CLOUD_ENV_ON_PREMISE
|
||||
keys.Provider = api.CLOUD_PROVIDER_PROXMOX
|
||||
keys.Brand = api.CLOUD_PROVIDER_PROXMOX
|
||||
keys.Hypervisor = api.HYPERVISOR_PROXMOX
|
||||
@@ -141,7 +146,7 @@ func (self *SProxmoxGuestDriver) GetChangeInstanceTypeStatus() ([]string, error)
|
||||
}
|
||||
|
||||
func (self *SProxmoxGuestDriver) GetRebuildRootStatus() ([]string, error) {
|
||||
return []string{}, cloudprovider.ErrNotSupported
|
||||
return []string{api.VM_READY}, nil
|
||||
}
|
||||
|
||||
func (self *SProxmoxGuestDriver) GetDeployStatus() ([]string, error) {
|
||||
@@ -188,3 +193,63 @@ func (self *SProxmoxGuestDriver) IsSupportCdrom(guest *models.SGuest) (bool, err
|
||||
func (self *SProxmoxGuestDriver) RequestRemoteUpdate(ctx context.Context, guest *models.SGuest, userCred mcclient.TokenCredential, replaceTags bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SProxmoxGuestDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, host *models.SHost, params *jsonutils.JSONDict) (jsonutils.JSONObject, error) {
|
||||
desc := guest.GetJsonDescAtHypervisor(ctx, host)
|
||||
if len(desc.Disks) > 0 && len(desc.Disks[0].TemplateId) == 0 {
|
||||
cdrom := guest.GetCdrom()
|
||||
if !gotypes.IsNil(cdrom) {
|
||||
storagecachedimage := models.StoragecachedimageManager.GetStoragecachedimage(desc.Disks[0].StoragecacheId, cdrom.ImageId)
|
||||
if storagecachedimage != nil {
|
||||
desc.ExternalImageId = storagecachedimage.ExternalId
|
||||
}
|
||||
}
|
||||
}
|
||||
return jsonutils.Marshal(desc), nil
|
||||
}
|
||||
|
||||
func (self *SProxmoxGuestDriver) RequestDeployGuestOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, task taskman.ITask) error {
|
||||
config, err := guest.GetDeployConfigOnHost(ctx, task.GetUserCred(), host, task.GetParams())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetDeployConfigOnHost")
|
||||
}
|
||||
log.Debugf("RequestDeployGuestOnHost: %s", config)
|
||||
|
||||
if !host.IsEsxiAgentReady() {
|
||||
return fmt.Errorf("No ESXi agent host")
|
||||
}
|
||||
|
||||
diskCat := guest.CategorizeDisks()
|
||||
if diskCat.Root == nil {
|
||||
return fmt.Errorf("no root disk???")
|
||||
}
|
||||
storage, _ := diskCat.Root.GetStorage()
|
||||
if storage == nil {
|
||||
return fmt.Errorf("root disk has no storage???")
|
||||
}
|
||||
|
||||
config.Add(jsonutils.NewString(host.ExternalId), "host_id")
|
||||
config.Add(jsonutils.NewString(host.AccessIp), "host_ip")
|
||||
|
||||
tags, _ := guest.GetAllUserMetadata()
|
||||
config.Set("tags", jsonutils.Marshal(tags))
|
||||
|
||||
account := host.GetCloudaccount()
|
||||
accessInfo, err := account.GetVCenterAccessInfo(storage.ExternalId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config.Add(jsonutils.Marshal(accessInfo), "datastore")
|
||||
config.Add(jsonutils.NewString(guest.ExternalId), "guest_ext_id")
|
||||
|
||||
url := "/disks/proxmox/deploy"
|
||||
|
||||
body := jsonutils.NewDict()
|
||||
body.Add(config, "disk")
|
||||
|
||||
header := task.GetTaskRequestHeader()
|
||||
|
||||
_, err = host.EsxiRequest(ctx, httputils.POST, url, header, body)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -86,23 +86,7 @@ func (self *SESXiHostDriver) CheckAndSetCacheImage(ctx context.Context, userCred
|
||||
}
|
||||
}
|
||||
|
||||
type contentStruct struct {
|
||||
ImageId string
|
||||
HostId string
|
||||
HostIp string
|
||||
SrcHostIp string
|
||||
SrcPath string
|
||||
SrcDatastore vcenter.SVCenterAccessInfo
|
||||
Datastore vcenter.SVCenterAccessInfo
|
||||
Format string
|
||||
IsForce bool
|
||||
StoragecacheId string
|
||||
ImageType string
|
||||
ImageExternalId string
|
||||
StorageCacheHostIp string
|
||||
}
|
||||
|
||||
content := contentStruct{}
|
||||
content := vcenter.ImageCacheInput{}
|
||||
content.ImageId = imageId
|
||||
content.HostId = host.Id
|
||||
content.HostIp = host.AccessIp
|
||||
|
||||
@@ -17,23 +17,16 @@ package hostdrivers
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"yunion.io/x/cloudmux/pkg/cloudprovider"
|
||||
"yunion.io/x/cloudmux/pkg/multicloud/esxi/vcenter"
|
||||
"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"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/httputils"
|
||||
)
|
||||
|
||||
type SProxmoxHostDriver struct {
|
||||
@@ -54,7 +47,7 @@ func (self *SProxmoxHostDriver) GetHypervisor() string {
|
||||
}
|
||||
|
||||
func (self *SProxmoxHostDriver) GetProvider() string {
|
||||
return api.CLOUD_PROVIDER_PROXMOX
|
||||
return api.CLOUD_PROVIDER_ONECLOUD
|
||||
}
|
||||
|
||||
func (self *SProxmoxHostDriver) ValidateDiskSize(storage *models.SStorage, sizeGb int) error {
|
||||
@@ -68,145 +61,101 @@ func (driver *SProxmoxHostDriver) GetStoragecacheQuota(host *models.SHost) int {
|
||||
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)
|
||||
obj, err := models.CachedimageManager.FetchById(input.ImageId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cacheImage := obj.(*models.SCachedimage)
|
||||
|
||||
cacheInput := vcenter.ImageCacheInput{}
|
||||
cacheInput.ImageId = input.ImageId
|
||||
cacheInput.ImageName = cacheImage.Name
|
||||
cacheInput.ImageExternalId = cacheImage.ExternalId
|
||||
cacheInput.Format = input.Format
|
||||
cacheInput.IsForce = input.IsForce
|
||||
cacheInput.ImageType = cacheImage.ImageType
|
||||
cacheInput.HostId = host.ExternalId
|
||||
cacheInput.HostIp = host.AccessIp
|
||||
cacheInput.Datastore, err = host.GetCloudaccount().GetVCenterAccessInfo(storageCache.ManagerId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
imageSize := int64(0)
|
||||
if !host.IsEsxiAgentReady() {
|
||||
return fmt.Errorf("fail to find valid ESXi agent")
|
||||
}
|
||||
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
body := jsonutils.NewDict()
|
||||
body.Add(jsonutils.Marshal(&cacheInput), "disk")
|
||||
|
||||
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))
|
||||
header := task.GetTaskRequestHeader()
|
||||
|
||||
log.Debugf("XXX Hold lockman key %p cachedimages %s-%s", ctx, storageCache.Id, input.ImageId)
|
||||
url := "/proxmox/disks/image_cache"
|
||||
|
||||
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, "%s", 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
|
||||
})
|
||||
_, err = host.EsxiRequest(ctx, httputils.POST, url, header, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SProxmoxHostDriver) RequestResizeDiskOnHost(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, sizeMb int64, task taskman.ITask) error {
|
||||
guest := disk.GetGuest()
|
||||
if guest == nil {
|
||||
return fmt.Errorf("unable to find guest has disk %s", disk.GetId())
|
||||
}
|
||||
|
||||
iVm, err := guest.GetIVM(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetIVM")
|
||||
}
|
||||
if iVm.GetStatus() == api.VM_RUNNING {
|
||||
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
|
||||
disks, err := iVm.GetIDisks()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetIDisk")
|
||||
}
|
||||
for i := range disks {
|
||||
if disks[i].GetGlobalId() == disk.ExternalId {
|
||||
err = disks[i].Resize(ctx, sizeMb)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Resize")
|
||||
}
|
||||
return jsonutils.Marshal(map[string]int64{"disk_size": sizeMb}), nil
|
||||
}
|
||||
}
|
||||
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "disk %s", disk.Name)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
spec := struct {
|
||||
HostInfo vcenter.SVCenterAccessInfo
|
||||
VMId string
|
||||
DiskId string
|
||||
SizeMb int64
|
||||
}{}
|
||||
|
||||
account := host.GetCloudaccount()
|
||||
accessInfo, err := account.GetVCenterAccessInfo(host.ExternalId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
spec.HostInfo = accessInfo
|
||||
spec.DiskId = disk.GetExternalId()
|
||||
spec.VMId = guest.GetExternalId()
|
||||
spec.SizeMb = sizeMb
|
||||
|
||||
body := jsonutils.NewDict()
|
||||
body.Add(jsonutils.Marshal(spec), "disk")
|
||||
|
||||
url := fmt.Sprintf("/disks/proxmox/resize/%s", disk.Id)
|
||||
header := task.GetTaskRequestHeader()
|
||||
|
||||
_, err = host.EsxiRequest(ctx, httputils.POST, url, header, body)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"yunion.io/x/cloudmux/pkg/multicloud/esxi"
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
@@ -58,20 +57,16 @@ var ipMaskLen int8 = 24
|
||||
func (account *SCloudaccount) PrepareEsxiHostNetwork(ctx context.Context, userCred mcclient.TokenCredential, zoneId string) error {
|
||||
cProvider, err := account.GetProvider(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "account.GetProvider")
|
||||
return errors.Wrap(err, "GetProvider")
|
||||
}
|
||||
// fetch esxiclient
|
||||
iregion, err := cProvider.GetOnPremiseIRegion()
|
||||
region, err := cProvider.GetOnPremiseIRegion()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "cProvider.GetOnPremiseIRegion")
|
||||
return errors.Wrap(err, "GetOnPremiseIRegion")
|
||||
}
|
||||
esxiClient, ok := iregion.(*esxi.SESXiClient)
|
||||
if !ok {
|
||||
return errors.Wrap(httperrors.ErrNotSupported, "not a esxi provider")
|
||||
}
|
||||
iHosts, err := esxiClient.GetIHosts()
|
||||
iHosts, err := region.GetIHosts()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "esxiClient.GetIHosts")
|
||||
return errors.Wrap(err, "GetIHosts")
|
||||
}
|
||||
|
||||
hostIps := make([]netutils.IPV4Addr, 0)
|
||||
|
||||
@@ -208,14 +208,18 @@ func (self *SGuestdisk) GetDiskJsonDescAtHost(ctx context.Context, host *SHost,
|
||||
Bps: self.Bps,
|
||||
Size: disk.DiskSize,
|
||||
PCIPath: disk.PCIPath,
|
||||
StorageId: disk.StorageId,
|
||||
}
|
||||
desc.TemplateId = disk.GetTemplateId()
|
||||
storage, _ := disk.GetStorage()
|
||||
desc.StorageType = storage.StorageType
|
||||
desc.StorageExternalId = storage.GetExternalId()
|
||||
desc.StoragecacheId = storage.StoragecacheId
|
||||
if len(desc.TemplateId) > 0 {
|
||||
storagecacheimg := StoragecachedimageManager.GetStoragecachedimage(storage.StoragecacheId, desc.TemplateId)
|
||||
if storagecacheimg != nil {
|
||||
desc.ImagePath = storagecacheimg.Path
|
||||
desc.ImageInfo.ImageExternalId = storagecacheimg.ExternalId
|
||||
}
|
||||
}
|
||||
if utils.IsInStringArray(host.HostType, []string{api.HOST_TYPE_HYPERVISOR, api.HOST_TYPE_CONTAINER}) {
|
||||
|
||||
@@ -1749,14 +1749,6 @@ func (manager *SGuestManager) validateCreateData(
|
||||
return nil, errors.Wrap(err, "checkGuestImage")
|
||||
}
|
||||
|
||||
if len(input.PreferManager) > 0 && len(input.Provider) == 0 {
|
||||
providerObj, err := CloudproviderManager.FetchById(input.PreferManager)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "zone fetch by id %s", input.PreferZone)
|
||||
}
|
||||
provider := providerObj.(*SCloudprovider)
|
||||
input.Provider = provider.Provider
|
||||
}
|
||||
if len(input.PreferZone) > 0 && len(input.Provider) == 0 {
|
||||
zoneObj, err := ZoneManager.FetchById(input.PreferZone)
|
||||
if err != nil {
|
||||
@@ -1773,6 +1765,15 @@ func (manager *SGuestManager) validateCreateData(
|
||||
region := regionObj.(*SCloudregion)
|
||||
input.Provider = region.Provider
|
||||
}
|
||||
// pve, vmware provider is OneCloud
|
||||
if len(input.PreferManager) > 0 && len(input.Provider) == 0 {
|
||||
providerObj, err := CloudproviderManager.FetchById(input.PreferManager)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "zone fetch by id %s", input.PreferZone)
|
||||
}
|
||||
provider := providerObj.(*SCloudprovider)
|
||||
input.Provider = provider.Provider
|
||||
}
|
||||
if len(input.Provider) == 0 {
|
||||
input.Provider = api.CLOUD_PROVIDER_ONECLOUD
|
||||
}
|
||||
@@ -1958,7 +1959,7 @@ func (manager *SGuestManager) validateCreateData(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
optionSystemHypervisor := []string{api.HYPERVISOR_KVM, api.HYPERVISOR_ESXI, api.HYPERVISOR_POD}
|
||||
optionSystemHypervisor := []string{api.HYPERVISOR_KVM, api.HYPERVISOR_ESXI, api.HYPERVISOR_PROXMOX, api.HYPERVISOR_POD}
|
||||
|
||||
if !utils.IsInStringArray(input.Hypervisor, optionSystemHypervisor) && len(input.Disks[0].ImageId) == 0 && len(input.Disks[0].SnapshotId) == 0 && input.Cdrom == "" {
|
||||
return nil, httperrors.NewBadRequestError("Miss operating system???")
|
||||
|
||||
@@ -41,8 +41,8 @@ func init() {
|
||||
func (self *CloudAccountSyncInfoTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
|
||||
cloudaccount := obj.(*models.SCloudaccount)
|
||||
|
||||
if cloudaccount.Provider == api.CLOUD_PROVIDER_VMWARE {
|
||||
cloudaccount.SetStatus(ctx, self.UserCred, api.CLOUD_PROVIDER_SYNC_NETWORK, "StartSyncVMwareNetworkTask")
|
||||
if cloudaccount.Provider == api.CLOUD_PROVIDER_VMWARE || cloudaccount.Provider == api.CLOUD_PROVIDER_PROXMOX {
|
||||
cloudaccount.SetStatus(ctx, self.UserCred, api.CLOUD_PROVIDER_SYNC_NETWORK, "StartSyncOnPremiseNetworkTask")
|
||||
zone, _ := self.Params.GetString("zone")
|
||||
err := cloudaccount.PrepareEsxiHostNetwork(ctx, self.UserCred, zone)
|
||||
if err != nil {
|
||||
|
||||
+21
-6
@@ -20,6 +20,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"yunion.io/x/cloudmux/pkg/multicloud/esxi/vcenter"
|
||||
"yunion.io/x/log"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/compute"
|
||||
@@ -47,9 +48,11 @@ var (
|
||||
type SEsxiAgent struct {
|
||||
agent.SBaseAgent
|
||||
|
||||
agentImageCache *storageman.SAgentImageCacheManager
|
||||
AgentStorage *storageman.SAgentStorage
|
||||
ListenNic netutils2.SNetInterface
|
||||
agentImageCache *storageman.SAgentImageCacheManager
|
||||
agentProxmoxCache *storageman.SAgentProxmoxCacheManager
|
||||
AgentStorage *storageman.SAgentStorage
|
||||
ProxmoxStorage *storageman.SProxmoxStorage
|
||||
ListenNic netutils2.SNetInterface
|
||||
}
|
||||
|
||||
func NewEsxiAgent() (*SEsxiAgent, error) {
|
||||
@@ -110,8 +113,11 @@ func (ea *SEsxiAgent) Start() error {
|
||||
}
|
||||
// add agent image cache
|
||||
ea.agentImageCache = storageman.NewAgentImageCacheManager(ea.CacheManager)
|
||||
ea.agentProxmoxCache = storageman.NewAgentProxmoxCacheManager(ea.CacheManager)
|
||||
ea.AgentStorage = storageman.NewAgentStorage(&storageman.SStorageManager{LocalStorageImagecacheManager: ea.CacheManager},
|
||||
ea, options.Options.AgentTempPath)
|
||||
ea.ProxmoxStorage = storageman.NewProxmoxStorage(&storageman.SStorageManager{LocalStorageImagecacheManager: ea.CacheManager},
|
||||
ea, options.Options.AgentTempPath)
|
||||
|
||||
cronManager := cronman.InitCronJobManager(false, options.Options.CronJobWorkerCount, options.Options.TimeZone)
|
||||
err = cronManager.AddJobEveryFewDays(
|
||||
@@ -147,6 +153,11 @@ func (agent *SEsxiAgent) AddImageCacheHandler(prefix string, app *appsrv.Applica
|
||||
auth.Authenticate(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
performImageCache(ctx, w, r, agent.agentImageCache.PrefetchImageCache)
|
||||
}))
|
||||
app.AddHandler("POST",
|
||||
fmt.Sprintf("%s/proxmox/disks/image_cache", prefix),
|
||||
auth.Authenticate(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
performImageCache(ctx, w, r, agent.agentProxmoxCache.PrefetchProxmoxImageCache)
|
||||
}))
|
||||
app.AddHandler("DELETE",
|
||||
fmt.Sprintf("%s/disks/image_cache", prefix),
|
||||
auth.Authenticate(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
@@ -162,13 +173,17 @@ func performImageCache(
|
||||
) {
|
||||
_, _, body := appsrv.FetchEnv(ctx, w, r)
|
||||
|
||||
disk, err := body.Get("disk")
|
||||
input := struct {
|
||||
Disk vcenter.ImageCacheInput
|
||||
}{}
|
||||
|
||||
err := body.Unmarshal(&input)
|
||||
if err != nil {
|
||||
httperrors.MissingParameterError(ctx, w, "disk")
|
||||
httperrors.BadRequestError(ctx, w, "unmarshal disk %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
hostutils.DelayTask(ctx, performTask, disk)
|
||||
hostutils.DelayTask(ctx, performTask, &input.Disk)
|
||||
hostutils.ResponseOk(ctx, w)
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ const (
|
||||
|
||||
func InitHandlers(app *appsrv.Application) {
|
||||
initESXIHandler(app)
|
||||
initProxmoxHandler(app)
|
||||
}
|
||||
|
||||
var defaultHandler = func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
@@ -55,7 +56,6 @@ func AgentPrefix(action string) string {
|
||||
}
|
||||
|
||||
func initESXIHandler(app *appsrv.Application) {
|
||||
|
||||
app.AddHandler("POST", AgentPrefix("upload"), auth.Authenticate(uploadHandler))
|
||||
app.AddHandler("POST", AgentPrefix("deploy"), auth.Authenticate(deployHandler))
|
||||
app.AddHandler("POST", IdAgentPrefix("delete"), auth.Authenticate(deleteHandler))
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/onecloud/pkg/appsrv"
|
||||
"yunion.io/x/onecloud/pkg/esxi"
|
||||
"yunion.io/x/onecloud/pkg/hostman/hostutils"
|
||||
"yunion.io/x/onecloud/pkg/hostman/storageman"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/pkg/appctx"
|
||||
"yunion.io/x/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
PROXMOX_PREFIX = "disks/proxmox"
|
||||
)
|
||||
|
||||
var defaultProxmoxHandler = func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
httperrors.NotImplementedError(ctx, w, "")
|
||||
return
|
||||
}
|
||||
|
||||
func IdProxmoxPrefix(action string) string {
|
||||
return fmt.Sprintf("%s/%s/<disk_id>", PROXMOX_PREFIX, action)
|
||||
}
|
||||
|
||||
func ProxmoxPrefix(action string) string {
|
||||
return fmt.Sprintf("%s/%s", PROXMOX_PREFIX, action)
|
||||
}
|
||||
|
||||
func initProxmoxHandler(app *appsrv.Application) {
|
||||
app.AddHandler("POST", ProxmoxPrefix("deploy"), auth.Authenticate(deployProxmoxHandler))
|
||||
app.AddHandler("POST", IdProxmoxPrefix("delete"), auth.Authenticate(deleteProxmoxHandler))
|
||||
app.AddHandler("POST", IdProxmoxPrefix("resize"), auth.Authenticate(resizeProxmoxHandler))
|
||||
}
|
||||
|
||||
func deployProxmoxHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
log.Debugf("enter deployProxmoxHandler")
|
||||
_, _, body := appsrv.FetchEnv(ctx, w, r)
|
||||
disk, err := body.Get("disk")
|
||||
if err != nil {
|
||||
httperrors.MissingParameterError(ctx, w, "miss disk")
|
||||
return
|
||||
}
|
||||
hostutils.DelayTask(ctx, esxi.EsxiAgent.ProxmoxStorage.AgentDeployGuest, disk)
|
||||
hostutils.ResponseOk(ctx, w)
|
||||
}
|
||||
|
||||
func deleteProxmoxHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
params, _, _ := appsrv.FetchEnv(ctx, w, r)
|
||||
diskId := params["<disk_id>"]
|
||||
disk, err := esxi.EsxiAgent.ProxmoxStorage.GetDiskById(diskId)
|
||||
if err != nil {
|
||||
httperrors.GeneralServerError(ctx, w, errors.Wrapf(err, "GetDiskById(%s)", diskId))
|
||||
return
|
||||
}
|
||||
if taskId := ctx.Value(appctx.APP_CONTEXT_KEY_TASK_ID); taskId == nil {
|
||||
if disk != nil {
|
||||
_, err := disk.Delete(ctx, nil)
|
||||
if err != nil {
|
||||
httperrors.GeneralServerError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
hostutils.ResponseOk(ctx, w)
|
||||
return
|
||||
}
|
||||
}
|
||||
hostutils.ResponseOk(ctx, w)
|
||||
hostutils.DelayTask(ctx, disk.Delete, nil)
|
||||
}
|
||||
|
||||
func resizeProxmoxHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
disk, diskInfo, err := proxmoxDiskAndDiskInfo(ctx, w, r)
|
||||
if err != nil {
|
||||
httperrors.GeneralServerError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
resizeDiskInfo := &storageman.SDiskResizeInput{
|
||||
DiskInfo: diskInfo,
|
||||
}
|
||||
resizeFunc := func(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
input, ok := params.(*storageman.SDiskResizeInput)
|
||||
if !ok {
|
||||
return nil, hostutils.ParamsError
|
||||
}
|
||||
return disk.Resize(ctx, input)
|
||||
}
|
||||
hostutils.DelayTask(ctx, resizeFunc, resizeDiskInfo)
|
||||
hostutils.ResponseOk(ctx, w)
|
||||
}
|
||||
|
||||
func proxmoxDiskAndDiskInfo(ctx context.Context, w http.ResponseWriter, r *http.Request) (storageman.IDisk, jsonutils.JSONObject, error) {
|
||||
params, _, body := appsrv.FetchEnv(ctx, w, r)
|
||||
diskId := params["<disk_id>"]
|
||||
disk, err := esxi.EsxiAgent.ProxmoxStorage.GetDiskById(diskId)
|
||||
if err != nil {
|
||||
return nil, nil, httperrors.NewGeneralError(errors.Wrapf(err, "GetDiskById(%s)", diskId))
|
||||
}
|
||||
diskInfo, err := body.Get("disk")
|
||||
if err != nil {
|
||||
return nil, nil, httperrors.NewMissingParameterError("miss disk")
|
||||
}
|
||||
return disk, diskInfo, nil
|
||||
}
|
||||
@@ -50,6 +50,8 @@ func GetIDisk(params DiskParams, driver string, readOnly bool) (IDisk, error) {
|
||||
case comapi.HYPERVISOR_ESXI:
|
||||
// ESXI does not support encrypted disk
|
||||
return NewVDDKDisk(params.VddkInfo, params.DiskInfo.Path, driver, readOnly)
|
||||
case comapi.HYPERVISOR_PROXMOX:
|
||||
return NewNbdkitDisk(params.VddkInfo, params.DiskInfo.Path, driver, readOnly)
|
||||
default:
|
||||
return NewKVMGuestDisk(params.DiskInfo, driver, readOnly)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package diskutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/hostman/guestfs/fsdriver"
|
||||
"yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
|
||||
"yunion.io/x/onecloud/pkg/util/qemuimg"
|
||||
)
|
||||
|
||||
type NbdkitDisk struct {
|
||||
Host string
|
||||
Port int
|
||||
User string
|
||||
Passwd string
|
||||
DiskPath string
|
||||
|
||||
Proc *Command
|
||||
NbdURI string
|
||||
|
||||
kvmDisk *SKVMGuestDisk
|
||||
readOnly bool
|
||||
deployDriver string
|
||||
}
|
||||
|
||||
func NewNbdkitDisk(vddkInfo *apis.VDDKConInfo, diskPath, deployDriver string, readOnly bool) (*NbdkitDisk, error) {
|
||||
return &NbdkitDisk{
|
||||
Host: vddkInfo.Host,
|
||||
Port: int(vddkInfo.Port),
|
||||
User: vddkInfo.User,
|
||||
Passwd: vddkInfo.Passwd,
|
||||
DiskPath: diskPath,
|
||||
deployDriver: deployDriver,
|
||||
readOnly: readOnly,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (vd *NbdkitDisk) Cleanup() {
|
||||
if vd.kvmDisk != nil {
|
||||
vd.kvmDisk.Cleanup()
|
||||
vd.kvmDisk = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (vd *NbdkitDisk) Connect(*apis.GuestDesc) error {
|
||||
flatFile, err := vd.ConnectBlockDevice()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "ConnectBlockDevice")
|
||||
}
|
||||
vd.kvmDisk, err = NewKVMGuestDisk(qemuimg.SImageInfo{Path: flatFile}, vd.deployDriver, vd.readOnly)
|
||||
if err != nil {
|
||||
vd.DisconnectBlockDevice()
|
||||
return errors.Wrap(err, "NewKVMGuestDisk")
|
||||
}
|
||||
err = vd.kvmDisk.Connect(nil)
|
||||
if err != nil {
|
||||
vd.DisconnectBlockDevice()
|
||||
return errors.Wrap(err, "kvmDisk connect")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vd *NbdkitDisk) ConnectWithDiskId(desc *apis.GuestDesc, diskId string) error {
|
||||
return vd.Connect(desc)
|
||||
}
|
||||
|
||||
func (vd *NbdkitDisk) Disconnect() error {
|
||||
if vd.kvmDisk != nil {
|
||||
if err := vd.kvmDisk.Disconnect(); err != nil {
|
||||
log.Errorf("kvm disk disconnect failed %s", err)
|
||||
}
|
||||
vd.kvmDisk.Cleanup()
|
||||
vd.kvmDisk = nil
|
||||
}
|
||||
return vd.DisconnectBlockDevice()
|
||||
}
|
||||
|
||||
func (vd *NbdkitDisk) MountRootfs() (fsdriver.IRootFsDriver, error) {
|
||||
if vd.kvmDisk == nil {
|
||||
return nil, fmt.Errorf("kvmDisk is nil")
|
||||
}
|
||||
return vd.kvmDisk.MountRootfs()
|
||||
}
|
||||
|
||||
func (vd *NbdkitDisk) UmountRootfs(fd fsdriver.IRootFsDriver) error {
|
||||
if vd.kvmDisk == nil {
|
||||
return nil
|
||||
}
|
||||
return vd.kvmDisk.UmountRootfs(fd)
|
||||
}
|
||||
|
||||
func (vd *NbdkitDisk) Mount() (err error) {
|
||||
_, err = vd.ConnectBlockDevice()
|
||||
return err
|
||||
}
|
||||
|
||||
func (vd *NbdkitDisk) Umount() error {
|
||||
if vd.Proc != nil {
|
||||
if !vd.Proc.Exited() {
|
||||
if err := vd.Proc.Kill(); err != nil {
|
||||
log.Errorf("kill nbdkit process failed: %s", err)
|
||||
}
|
||||
}
|
||||
if err := vd.Proc.Wait(); err != nil {
|
||||
log.Warningf("wait nbdkit process failed: %s", err)
|
||||
}
|
||||
vd.Proc = nil
|
||||
}
|
||||
vd.NbdURI = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vd *NbdkitDisk) ExecProg() error {
|
||||
args := []string{
|
||||
"--foreground",
|
||||
"--exit-with-parent",
|
||||
"-v",
|
||||
"ssh",
|
||||
"verify-remote-host=false",
|
||||
fmt.Sprintf("host=%s", vd.Host),
|
||||
fmt.Sprintf("user=%s", vd.User),
|
||||
fmt.Sprintf("path=%s", vd.DiskPath),
|
||||
}
|
||||
if vd.readOnly {
|
||||
args = append([]string{"-r"}, args...)
|
||||
}
|
||||
if vd.Port > 0 {
|
||||
args = append(args, fmt.Sprintf("port=%d", vd.Port))
|
||||
}
|
||||
if len(vd.Passwd) > 0 {
|
||||
args = append(args, fmt.Sprintf("password=%s", vd.Passwd))
|
||||
}
|
||||
cmd := NewCommand("nbdkit", args...)
|
||||
log.Debugf("command to mount: %s", cmd)
|
||||
vd.Proc = cmd
|
||||
vd.NbdURI = ""
|
||||
err := vd.Proc.Start()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "vd.Proc.Start")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vd *NbdkitDisk) WaitMounted() error {
|
||||
timeout := time.After(30 * time.Second)
|
||||
for {
|
||||
select {
|
||||
case <-timeout:
|
||||
return errors.Errorf("wait nbdkit ready timeout: %s", vd.Proc.stdouterr.String())
|
||||
default:
|
||||
if vd.Proc != nil {
|
||||
output := vd.Proc.stdouterr.String()
|
||||
if nbdURI, ok := parseNbdkitOutputNbdURI(output); ok {
|
||||
vd.NbdURI = nbdURI
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if vd.Proc != nil && vd.Proc.Exited() {
|
||||
backup := vd.Proc.stdouterr.String()
|
||||
return errors.Errorf("nbdkit exited unexpectedly: %s", backup)
|
||||
}
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
nbdkitURIRegex = regexp.MustCompile(`nbd://(?:127\.0\.0\.1|localhost):(\d{2,5})`)
|
||||
nbdkitPortRegexList = []*regexp.Regexp{
|
||||
regexp.MustCompile(`(?:127\.0\.0\.1|localhost):(\d{2,5})`),
|
||||
regexp.MustCompile(`\bbound\s+to\s+IP\s+address\s+[^\s:]+:(\d{2,5})\b`),
|
||||
regexp.MustCompile(`\bport(?:\s+number)?\s*[=:]?\s*(\d{2,5})\b`),
|
||||
}
|
||||
)
|
||||
|
||||
// nbdkit: debug: bound to IP address <any>:10809 (2 socket(s))
|
||||
func parseNbdkitOutputNbdURI(output string) (string, bool) {
|
||||
if matched := nbdkitURIRegex.FindStringSubmatch(output); len(matched) == 2 {
|
||||
port, err := strconv.Atoi(matched[1])
|
||||
if err == nil && port > 0 {
|
||||
return fmt.Sprintf("nbd://127.0.0.1:%d/", port), true
|
||||
}
|
||||
}
|
||||
for _, regex := range nbdkitPortRegexList {
|
||||
if matched := regex.FindStringSubmatch(output); len(matched) == 2 {
|
||||
port, err := strconv.Atoi(matched[1])
|
||||
if err == nil && port > 0 {
|
||||
return fmt.Sprintf("nbd://127.0.0.1:%d/", port), true
|
||||
}
|
||||
}
|
||||
}
|
||||
if idx := strings.Index(output, "nbd://"); idx >= 0 {
|
||||
candidate := output[idx:]
|
||||
if end := strings.IndexAny(candidate, " \n\r\t"); end >= 0 {
|
||||
candidate = candidate[:end]
|
||||
}
|
||||
if strings.HasPrefix(candidate, "nbd://") && strings.Contains(candidate, ":") {
|
||||
parts := strings.Split(candidate, ":")
|
||||
port, err := strconv.Atoi(parts[len(parts)-1])
|
||||
if err == nil && port > 0 {
|
||||
return fmt.Sprintf("nbd://127.0.0.1:%d/", port), true
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// ConnectBlockDevice starts nbdkit ssh plugin and returns a local NBD URI.
|
||||
func (vd *NbdkitDisk) ConnectBlockDevice() (string, error) {
|
||||
if vd.Proc != nil && !vd.Proc.Exited() && len(vd.NbdURI) > 0 {
|
||||
return vd.NbdURI, nil
|
||||
}
|
||||
if err := vd.ExecProg(); err != nil {
|
||||
return "", errors.Wrap(err, "ExecProg")
|
||||
}
|
||||
if err := vd.WaitMounted(); err != nil {
|
||||
return "", errors.Wrap(err, "WaitMounted")
|
||||
}
|
||||
log.Infof("disk mounted by nbdkit over %s", vd.NbdURI)
|
||||
return vd.NbdURI, nil
|
||||
}
|
||||
|
||||
func (vd *NbdkitDisk) DisconnectBlockDevice() error {
|
||||
if vd.Proc != nil {
|
||||
return vd.Umount()
|
||||
}
|
||||
return fmt.Errorf("nbdkit disk has not connected")
|
||||
}
|
||||
|
||||
func (vd *NbdkitDisk) DeployGuestfs(req *apis.DeployParams) (res *apis.DeployGuestFsResponse, err error) {
|
||||
return vd.kvmDisk.DeployGuestfs(req)
|
||||
}
|
||||
|
||||
func (d *NbdkitDisk) ResizeFs(req *apis.ResizeFsParams) (*apis.Empty, error) {
|
||||
return d.kvmDisk.ResizeFs(req)
|
||||
}
|
||||
|
||||
func (d *NbdkitDisk) FormatFs(req *apis.FormatFsParams) (*apis.Empty, error) {
|
||||
return d.kvmDisk.FormatFs(req)
|
||||
}
|
||||
|
||||
func (d *NbdkitDisk) SaveToGlance(req *apis.SaveToGlanceParams) (*apis.SaveToGlanceResponse, error) {
|
||||
return d.kvmDisk.SaveToGlance(req)
|
||||
}
|
||||
|
||||
func (d *NbdkitDisk) ProbeImageInfo(req *apis.ProbeImageInfoPramas) (*apis.ImageInfo, error) {
|
||||
return d.kvmDisk.ProbeImageInfo(req)
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package diskutils
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestParseNbdkitOutputNbdURI(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
output string
|
||||
want string
|
||||
ok bool
|
||||
}{
|
||||
{
|
||||
name: "bound to any ip address",
|
||||
output: "nbdkit: debug: bound to IP address <any>:10809 (2 socket(s))",
|
||||
want: "nbd://127.0.0.1:10809/",
|
||||
ok: true,
|
||||
},
|
||||
{
|
||||
name: "direct nbd uri",
|
||||
output: "ready at nbd://localhost:10810",
|
||||
want: "nbd://127.0.0.1:10810/",
|
||||
ok: true,
|
||||
},
|
||||
{
|
||||
name: "port key value",
|
||||
output: "nbdkit: listening port=10811",
|
||||
want: "nbd://127.0.0.1:10811/",
|
||||
ok: true,
|
||||
},
|
||||
{
|
||||
name: "cannot parse",
|
||||
output: "nbdkit: debug: waiting for connection",
|
||||
want: "",
|
||||
ok: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
got, ok := parseNbdkitOutputNbdURI(tc.output)
|
||||
if ok != tc.ok {
|
||||
t.Fatalf("%s: ok mismatch, got %v want %v", tc.name, ok, tc.ok)
|
||||
}
|
||||
if got != tc.want {
|
||||
t.Fatalf("%s: uri mismatch, got %q want %q", tc.name, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package storageman
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/cloudmux/pkg/cloudprovider"
|
||||
"yunion.io/x/cloudmux/pkg/multicloud/esxi/vcenter"
|
||||
"yunion.io/x/cloudmux/pkg/multicloud/proxmox"
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/compute"
|
||||
deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
|
||||
"yunion.io/x/onecloud/pkg/hostman/hostdeployer/deployclient"
|
||||
"yunion.io/x/onecloud/pkg/hostman/hostutils"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
)
|
||||
|
||||
type SProxmoxDisk struct {
|
||||
SLocalDisk
|
||||
}
|
||||
|
||||
func NewProxmoxDisk(storage IStorage, id string) *SProxmoxDisk {
|
||||
return &SProxmoxDisk{*NewLocalDisk(storage, id)}
|
||||
}
|
||||
|
||||
func (sd *SProxmoxDisk) PrepareSaveToGlance(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
p, ok := params.(PrepareSaveToGlanceParams)
|
||||
if !ok {
|
||||
return nil, errors.Wrap(hostutils.ParamsError, "Resize params format error")
|
||||
}
|
||||
storage := sd.Storage.(*SAgentStorage)
|
||||
return storage.PrepareSaveToGlance(ctx, p.TaskId, p.DiskInfo)
|
||||
}
|
||||
|
||||
func (sd *SProxmoxDisk) Resize(ctx context.Context, params *SDiskResizeInput) (jsonutils.JSONObject, error) {
|
||||
body := params.DiskInfo
|
||||
type sResize struct {
|
||||
SizeMb int64 `json:"size_mb"`
|
||||
HostInfo vcenter.SVCenterAccessInfo
|
||||
VMId string `json:"vm_id"`
|
||||
DiskId string `json:"disk_id"`
|
||||
}
|
||||
resize := sResize{}
|
||||
err := body.Unmarshal(&resize)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "%s: unmarshal to sResize", hostutils.ParamsError)
|
||||
}
|
||||
|
||||
client, err := proxmox.NewProxmoxClientFromAccessInfo(&resize.HostInfo)
|
||||
if err != nil {
|
||||
return nil, httperrors.NewInputParameterError("info of host_info error")
|
||||
}
|
||||
host, err := client.GetHost(resize.HostInfo.PrivateId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "fail to find host by ip %s", resize.HostInfo.PrivateId)
|
||||
}
|
||||
ivm, err := host.GetIVMById(resize.VMId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "fail to find vm by ID %s", resize.VMId)
|
||||
}
|
||||
disks, err := ivm.GetIDisks()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "fail to get idisks of vm %s", resize.VMId)
|
||||
}
|
||||
var disk cloudprovider.ICloudDisk = nil
|
||||
for i := range disks {
|
||||
if disks[i].GetGlobalId() == resize.DiskId {
|
||||
disk = disks[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if disk == nil {
|
||||
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "fail to find disk by ID %s", resize.DiskId)
|
||||
}
|
||||
err = disk.Resize(ctx, resize.SizeMb)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "fail to resize disk %s", resize.DiskId)
|
||||
}
|
||||
|
||||
desc := jsonutils.NewDict()
|
||||
desc.Add(jsonutils.NewInt(resize.SizeMb), "disk_size")
|
||||
// try to resize partition
|
||||
err = resizeProxmoxPartition(ctx, host, disk, resize.HostInfo)
|
||||
if err != nil {
|
||||
log.Errorf("unable to ResizePartition: %v", err)
|
||||
}
|
||||
return desc, nil
|
||||
}
|
||||
|
||||
func resizeProxmoxPartition(ctx context.Context, host *proxmox.SHost, disk cloudprovider.ICloudDisk, accessInfo vcenter.SVCenterAccessInfo) error {
|
||||
diskPath := disk.GetAccessPath()
|
||||
diskInfo := deployapi.DiskInfo{
|
||||
Path: diskPath,
|
||||
}
|
||||
vddkInfo := deployapi.VDDKConInfo{
|
||||
Host: host.GetAccessIp(),
|
||||
Port: 22,
|
||||
User: strings.TrimSuffix(accessInfo.Account, "@pam"),
|
||||
Passwd: accessInfo.Password,
|
||||
}
|
||||
_, err := deployclient.GetDeployClient().ResizeFs(ctx, &deployapi.ResizeFsParams{
|
||||
DiskInfo: &diskInfo,
|
||||
Hypervisor: compute.HYPERVISOR_PROXMOX,
|
||||
VddkInfo: &vddkInfo,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to ResizeFs")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -41,44 +41,23 @@ func NewAgentImageCacheManager(manger IImageCacheManger) *SAgentImageCacheManage
|
||||
return &SAgentImageCacheManager{manger}
|
||||
}
|
||||
|
||||
type sImageCacheData struct {
|
||||
ImageId string
|
||||
HostId string
|
||||
HostIp string
|
||||
SrcHostIp string
|
||||
SrcPath string
|
||||
SrcDatastore vcenter.SVCenterAccessInfo
|
||||
Datastore vcenter.SVCenterAccessInfo
|
||||
Format string
|
||||
IsForce bool
|
||||
StoragecacheId string
|
||||
ImageType string
|
||||
ImageExternalId string
|
||||
StorageCacheHostIp string
|
||||
}
|
||||
|
||||
func (c *SAgentImageCacheManager) PrefetchImageCache(ctx context.Context, data interface{}) (jsonutils.JSONObject, error) {
|
||||
dataDict, ok := data.(*jsonutils.JSONDict)
|
||||
input, ok := data.(*vcenter.ImageCacheInput)
|
||||
if !ok {
|
||||
return nil, errors.Wrap(hostutils.ParamsError, "PrefetchImageCache data format error")
|
||||
}
|
||||
idata := new(sImageCacheData)
|
||||
err := dataDict.Unmarshal(idata)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "%s: unmarshal to sImageCacheData error")
|
||||
lockman.LockRawObject(ctx, input.HostId, input.ImageId)
|
||||
defer lockman.ReleaseRawObject(ctx, input.HostId, input.ImageId)
|
||||
if cloudprovider.TImageType(input.ImageType) == cloudprovider.ImageTypeSystem {
|
||||
return c.perfetchTemplateVMImageCache(ctx, input)
|
||||
}
|
||||
lockman.LockRawObject(ctx, idata.HostId, idata.ImageId)
|
||||
defer lockman.ReleaseRawObject(ctx, idata.HostId, idata.ImageId)
|
||||
if cloudprovider.TImageType(idata.ImageType) == cloudprovider.ImageTypeSystem {
|
||||
return c.perfetchTemplateVMImageCache(ctx, idata)
|
||||
if len(input.SrcHostIp) != 0 {
|
||||
return c.prefetchImageCacheByCopy(ctx, input)
|
||||
}
|
||||
if len(idata.SrcHostIp) != 0 {
|
||||
return c.prefetchImageCacheByCopy(ctx, idata)
|
||||
}
|
||||
return c.prefetchImageCacheByUpload(ctx, idata, dataDict)
|
||||
return c.prefetchImageCacheByUpload(ctx, input)
|
||||
}
|
||||
|
||||
func (c *SAgentImageCacheManager) prefetchImageCacheByCopy(ctx context.Context, data *sImageCacheData) (jsonutils.JSONObject, error) {
|
||||
func (c *SAgentImageCacheManager) prefetchImageCacheByCopy(ctx context.Context, data *vcenter.ImageCacheInput) (jsonutils.JSONObject, error) {
|
||||
client, err := esxi.NewESXiClientFromAccessInfo(ctx, &data.Datastore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -147,39 +126,39 @@ func (c *SAgentImageCacheManager) prefetchImageCacheByCopy(ctx context.Context,
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (c *SAgentImageCacheManager) prefetchImageCacheByUpload(ctx context.Context, data *sImageCacheData,
|
||||
origin *jsonutils.JSONDict) (jsonutils.JSONObject, error) {
|
||||
|
||||
input := api.CacheImageInput{}
|
||||
origin.Unmarshal(&input)
|
||||
localImage, err := c.imageCacheManger.PrefetchImageCache(ctx, input)
|
||||
func (c *SAgentImageCacheManager) prefetchImageCacheByUpload(ctx context.Context, input *vcenter.ImageCacheInput) (jsonutils.JSONObject, error) {
|
||||
data := api.CacheImageInput{}
|
||||
err := jsonutils.Update(&data, input)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "jsonutils.Update")
|
||||
}
|
||||
localImage, err := c.imageCacheManger.PrefetchImageCache(ctx, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
localImgPath, _ := localImage.GetString("path")
|
||||
//localImgSize, _ := localImage.Int("size")
|
||||
|
||||
client, err := esxi.NewESXiClientFromAccessInfo(ctx, &data.Datastore)
|
||||
client, err := esxi.NewESXiClientFromAccessInfo(ctx, &input.Datastore)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "esxi.NewESXiClientFromJson")
|
||||
}
|
||||
host, err := client.FindHostByIp(data.HostIp)
|
||||
host, err := client.FindHostByIp(input.HostIp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ds, err := host.FindDataStoreById(data.Datastore.PrivateId)
|
||||
ds, err := host.FindDataStoreById(input.Datastore.PrivateId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SHost.FindDataStoreById")
|
||||
}
|
||||
|
||||
format, _ := origin.GetString("format")
|
||||
if format == "" {
|
||||
format = "vmdk"
|
||||
if input.Format == "" {
|
||||
input.Format = "vmdk"
|
||||
}
|
||||
remotePath := fmt.Sprintf("image_cache/%s.%s", data.ImageId, format)
|
||||
remotePath := fmt.Sprintf("image_cache/%s.%s", input.ImageId, input.Format)
|
||||
// check if dst image is exist
|
||||
exists := false
|
||||
if format == "vmdk" {
|
||||
if input.Format == "vmdk" {
|
||||
err = ds.CheckVmdk(ctx, remotePath)
|
||||
if err != nil {
|
||||
log.Debugf("ds.CheckVmdk failed: %s", err)
|
||||
@@ -197,8 +176,8 @@ func (c *SAgentImageCacheManager) prefetchImageCacheByUpload(ctx context.Context
|
||||
}
|
||||
}
|
||||
log.Debugf("exist: %t, remotePath: %s", exists, remotePath)
|
||||
if !exists || data.IsForce {
|
||||
if format == "iso" {
|
||||
if !exists || input.IsForce {
|
||||
if input.Format == "iso" {
|
||||
err := ds.ImportISO(ctx, localImgPath, remotePath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "SDatastore.ImportISO %s -> %s", localImage, remotePath)
|
||||
@@ -214,7 +193,7 @@ func (c *SAgentImageCacheManager) prefetchImageCacheByUpload(ctx context.Context
|
||||
remoteImg := localImage.(*jsonutils.JSONDict)
|
||||
remoteImg.Add(jsonutils.NewString(remotePath), "path")
|
||||
|
||||
_, err = hostutils.RemoteStoragecacheCacheImage(ctx, data.StoragecacheId, data.ImageId, "active", remotePath)
|
||||
_, err = hostutils.RemoteStoragecacheCacheImage(ctx, input.StoragecacheId, input.ImageId, "active", remotePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -222,7 +201,7 @@ func (c *SAgentImageCacheManager) prefetchImageCacheByUpload(ctx context.Context
|
||||
return remoteImg, nil
|
||||
}
|
||||
|
||||
func (c *SAgentImageCacheManager) perfetchTemplateVMImageCache(ctx context.Context, data *sImageCacheData) (jsonutils.JSONObject, error) {
|
||||
func (c *SAgentImageCacheManager) perfetchTemplateVMImageCache(ctx context.Context, data *vcenter.ImageCacheInput) (jsonutils.JSONObject, error) {
|
||||
client, err := esxi.NewESXiClientFromAccessInfo(ctx, &data.Datastore)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "esxi.NewESXiClientFromJson")
|
||||
@@ -237,29 +216,22 @@ func (c *SAgentImageCacheManager) perfetchTemplateVMImageCache(ctx context.Conte
|
||||
}
|
||||
|
||||
func (c *SAgentImageCacheManager) DeleteImageCache(ctx context.Context, data interface{}) (jsonutils.JSONObject, error) {
|
||||
dataDict, ok := data.(*jsonutils.JSONDict)
|
||||
input, ok := data.(*vcenter.ImageCacheInput)
|
||||
if !ok {
|
||||
return nil, errors.Wrap(hostutils.ParamsError, "DeleteImageCache data format error")
|
||||
}
|
||||
var (
|
||||
imageID, _ = dataDict.GetString("image_id")
|
||||
hostIP, _ = dataDict.GetString("host_ip")
|
||||
dsInfo, _ = dataDict.Get("ds_info")
|
||||
)
|
||||
|
||||
client, _, err := esxi.NewESXiClientFromJson(ctx, dsInfo)
|
||||
client, _, err := esxi.NewESXiClientFromJson(ctx, jsonutils.Marshal(input.Datastore))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
host, err := client.FindHostByIp(hostIP)
|
||||
host, err := client.FindHostByIp(input.HostIp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dsID, _ := dsInfo.GetString("private_id")
|
||||
ds, err := host.FindDataStoreById(dsID)
|
||||
ds, err := host.FindDataStoreById(input.Datastore.PrivateId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
remotePath := fmt.Sprintf("image_cache/%s.vmdk", imageID)
|
||||
remotePath := fmt.Sprintf("image_cache/%s.vmdk", input.ImageId)
|
||||
return nil, ds.Delete(ctx, remotePath)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package storageman
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/cloudmux/pkg/cloudprovider"
|
||||
"yunion.io/x/cloudmux/pkg/multicloud/esxi/vcenter"
|
||||
"yunion.io/x/cloudmux/pkg/multicloud/proxmox"
|
||||
"yunion.io/x/jsonutils"
|
||||
"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/lockman"
|
||||
"yunion.io/x/onecloud/pkg/hostman/hostutils"
|
||||
)
|
||||
|
||||
type SAgentProxmoxCacheManager struct {
|
||||
imageCacheManger IImageCacheManger
|
||||
}
|
||||
|
||||
func NewAgentProxmoxCacheManager(manger IImageCacheManger) *SAgentProxmoxCacheManager {
|
||||
return &SAgentProxmoxCacheManager{manger}
|
||||
}
|
||||
|
||||
func (c *SAgentProxmoxCacheManager) PrefetchProxmoxImageCache(ctx context.Context, data interface{}) (jsonutils.JSONObject, error) {
|
||||
input, ok := data.(*vcenter.ImageCacheInput)
|
||||
if !ok {
|
||||
return nil, errors.Wrap(hostutils.ParamsError, "PrefetchImageCache data format error")
|
||||
}
|
||||
lockman.LockRawObject(ctx, input.HostId, input.ImageId)
|
||||
defer lockman.ReleaseRawObject(ctx, input.HostId, input.ImageId)
|
||||
if cloudprovider.TImageType(input.ImageType) == cloudprovider.ImageTypeSystem {
|
||||
return c.prefetchProxmoxTemplateVMImageCache(ctx, input)
|
||||
}
|
||||
return c.prefetchProxmoxImageCacheByUpload(ctx, input)
|
||||
}
|
||||
|
||||
func (c *SAgentProxmoxCacheManager) prefetchProxmoxTemplateVMImageCache(ctx context.Context, data *vcenter.ImageCacheInput) (jsonutils.JSONObject, error) {
|
||||
client, err := proxmox.NewProxmoxClientFromAccessInfo(&data.Datastore)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "esxi.NewESXiClientFromJson")
|
||||
}
|
||||
images, err := client.GetTemplateImages("")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetClusterVmResources")
|
||||
}
|
||||
for i := range images {
|
||||
if images[i].GetGlobalId() == data.ImageExternalId {
|
||||
ret := jsonutils.NewDict()
|
||||
ret.Set("image_id", jsonutils.NewString(images[i].GetGlobalId()))
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "no template image found for %q", data.ImageExternalId)
|
||||
}
|
||||
|
||||
func (c *SAgentProxmoxCacheManager) prefetchProxmoxImageCacheByUpload(ctx context.Context, input *vcenter.ImageCacheInput) (jsonutils.JSONObject, error) {
|
||||
data := api.CacheImageInput{}
|
||||
err := jsonutils.Update(&data, input)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "jsonutils.Update")
|
||||
}
|
||||
localImage, err := c.imageCacheManger.PrefetchImageCache(ctx, data)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "PrefetchImageCache %s", input.ImageId)
|
||||
}
|
||||
|
||||
localImgPath, _ := localImage.GetString("path")
|
||||
|
||||
client, err := proxmox.NewProxmoxClientFromAccessInfo(&input.Datastore)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "esxi.NewESXiClientFromJson")
|
||||
}
|
||||
host, err := client.GetHost(input.HostId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetHost %s", input.HostId)
|
||||
}
|
||||
|
||||
storages, err := client.GetStoragesByHost(host.Node)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetStorage %s", input.StoragecacheId)
|
||||
}
|
||||
|
||||
if len(input.Format) == 0 {
|
||||
input.Format = "qcow2"
|
||||
}
|
||||
|
||||
content := "import"
|
||||
if input.Format == "iso" {
|
||||
content = "iso"
|
||||
}
|
||||
|
||||
ret := jsonutils.NewDict()
|
||||
|
||||
var storage *proxmox.SStorage = nil
|
||||
maxSize := int64(0)
|
||||
for i := range storages {
|
||||
if strings.Contains(storages[i].Content, content) {
|
||||
image, err := storages[i].SearchImage(content, input.ImageId, input.ImageName, input.Format)
|
||||
if err != nil {
|
||||
if errors.Cause(err) != cloudprovider.ErrNotFound {
|
||||
return nil, errors.Wrapf(err, "SearchImage %s", input.ImageId)
|
||||
}
|
||||
if storages[i].MaxDisk-storages[i].Disk > maxSize {
|
||||
maxSize = storages[i].MaxDisk - storages[i].Disk
|
||||
storage = &storages[i]
|
||||
}
|
||||
continue
|
||||
}
|
||||
ret.Set("path", jsonutils.NewString(image.Volid))
|
||||
ret.Set("name", jsonutils.NewString(image.GetName()))
|
||||
ret.Set("image_id", jsonutils.NewString(image.GetGlobalId()))
|
||||
ret.Set("size", jsonutils.NewInt(image.Size))
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
if gotypes.IsNil(storage) {
|
||||
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "no valid storage found")
|
||||
}
|
||||
|
||||
file, err := os.Open(localImgPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Open %s", localImgPath)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
err = client.ImportImage(host.Node, storage.Storage, input.ImageId, input.Format, file)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "ImportImage %s", input.ImageId)
|
||||
}
|
||||
|
||||
var image *proxmox.SImage = nil
|
||||
for i := 0; i < 3; i++ {
|
||||
image, err = storage.SearchImage(content, input.ImageId, input.ImageName, input.Format)
|
||||
if err != nil {
|
||||
if errors.Cause(err) != cloudprovider.ErrNotFound {
|
||||
return nil, errors.Wrapf(err, "SearchImage %s", input.ImageId)
|
||||
}
|
||||
time.Sleep(time.Second * 10)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if gotypes.IsNil(image) {
|
||||
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "search image %s %s %s %s after upload", content, input.ImageId, input.ImageName, input.Format)
|
||||
}
|
||||
|
||||
ret.Set("name", jsonutils.NewString(image.GetName()))
|
||||
ret.Set("image_id", jsonutils.NewString(image.GetGlobalId()))
|
||||
ret.Set("size", jsonutils.NewInt(image.Size))
|
||||
return ret, nil
|
||||
}
|
||||
@@ -236,6 +236,7 @@ func (self esxiVm) GetName() string {
|
||||
|
||||
type SHostDatastore struct {
|
||||
HostIp string
|
||||
HostId string
|
||||
Datastore vcenter.SVCenterAccessInfo
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,353 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package storageman
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/cloudmux/pkg/cloudprovider"
|
||||
"yunion.io/x/cloudmux/pkg/multicloud/proxmox"
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/agent/iagent"
|
||||
deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
|
||||
"yunion.io/x/onecloud/pkg/hostman/hostdeployer/deployclient"
|
||||
"yunion.io/x/onecloud/pkg/hostman/hostutils"
|
||||
"yunion.io/x/onecloud/pkg/hostman/options"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/seclib"
|
||||
"yunion.io/x/pkg/utils"
|
||||
)
|
||||
|
||||
type SProxmoxStorage struct {
|
||||
SLocalStorage
|
||||
agent iagent.IAgent
|
||||
}
|
||||
|
||||
func NewProxmoxStorage(manager *SStorageManager, agent iagent.IAgent, path string) *SProxmoxStorage {
|
||||
s := &SProxmoxStorage{SLocalStorage: *NewLocalStorage(manager, path, 0)}
|
||||
s.agent = agent
|
||||
s.checkDirC(path)
|
||||
return s
|
||||
}
|
||||
|
||||
func (ps *SProxmoxStorage) GetDiskById(diskId string) (IDisk, error) {
|
||||
return NewProxmoxDisk(ps, diskId), nil
|
||||
}
|
||||
|
||||
func (ps *SProxmoxStorage) isExist(dir string) bool {
|
||||
_, err := os.Stat(dir)
|
||||
if err != nil {
|
||||
if os.IsExist(err) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (ps *SProxmoxStorage) checkDirC(dir string) error {
|
||||
if ps.isExist(dir) {
|
||||
return nil
|
||||
}
|
||||
return os.MkdirAll(dir, 0770)
|
||||
}
|
||||
|
||||
func (ps *SProxmoxStorage) AgentDeployGuest(ctx context.Context, data interface{}) (jsonutils.JSONObject, error) {
|
||||
dataDict := data.(*jsonutils.JSONDict)
|
||||
log.Debugf("dataDict: %s", dataDict)
|
||||
deployInfo := SDeployInfo{}
|
||||
err := dataDict.Unmarshal(&deployInfo)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Unmarshal DeployInfo")
|
||||
}
|
||||
var vm cloudprovider.ICloudVM = nil
|
||||
var needDeploy = true
|
||||
switch deployInfo.Action {
|
||||
case "create":
|
||||
vm, needDeploy, err = ps.agentCreateGuest(ctx, dataDict)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "agentCreateGuest")
|
||||
}
|
||||
case "rebuild":
|
||||
vm, err = ps.agentRebuildRoot(ctx, dataDict)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "agentRebuildRoot")
|
||||
}
|
||||
case "deploy":
|
||||
vm, err = ps.agentGetVm(ctx, dataDict)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "agentGetVm")
|
||||
}
|
||||
default:
|
||||
return nil, errors.Errorf("invalid action: %s", deployInfo.Action)
|
||||
}
|
||||
|
||||
disks, err := vm.GetIDisks()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "VM.GetIDisks")
|
||||
}
|
||||
if len(disks) == 0 {
|
||||
return nil, errors.Error(fmt.Sprintf("no such disks for vm %s", vm.GetId()))
|
||||
}
|
||||
|
||||
var deploy *deployapi.DeployGuestFsResponse
|
||||
if needDeploy {
|
||||
accountPassword, _ := utils.DescryptAESBase64(deployInfo.Datastore.VcenterId, deployInfo.Datastore.Password)
|
||||
vddkInfo := deployapi.VDDKConInfo{
|
||||
Host: deployInfo.HostIp,
|
||||
Port: 22,
|
||||
User: strings.TrimSuffix(deployInfo.Datastore.Account, "@pam"),
|
||||
Passwd: accountPassword,
|
||||
}
|
||||
guestDesc := deployapi.GuestDesc{}
|
||||
err = dataDict.Unmarshal(&guestDesc, "desc")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "%s: unmarshal to guestDesc", hostutils.ParamsError.Error())
|
||||
}
|
||||
|
||||
guestDesc.Hypervisor = api.HYPERVISOR_PROXMOX
|
||||
|
||||
key := deployapi.SSHKeys{
|
||||
PublicKey: deployInfo.PublicKey,
|
||||
DeletePublicKey: deployInfo.DeletePublicKey,
|
||||
AdminPublicKey: deployInfo.AdminPublicKey,
|
||||
ProjectPublicKey: deployInfo.ProjectPublicKey,
|
||||
}
|
||||
|
||||
deployArray := make([]*deployapi.DeployContent, 0)
|
||||
for _, deploy := range deployInfo.Deploys {
|
||||
deployArray = append(deployArray, &deployapi.DeployContent{
|
||||
Path: deploy.Path,
|
||||
Content: deploy.Content,
|
||||
Action: deploy.Action,
|
||||
})
|
||||
}
|
||||
|
||||
isRandomPassword := false
|
||||
passwd := deployInfo.Password
|
||||
resetPassword := deployInfo.ResetPassword
|
||||
if resetPassword && len(passwd) == 0 {
|
||||
passwd = seclib.RandomPassword(12)
|
||||
isRandomPassword = true
|
||||
}
|
||||
|
||||
if deployInfo.DeployTelegraf && deployInfo.TelegrafConf == "" {
|
||||
return nil, errors.Errorf("missing telegraf_conf")
|
||||
}
|
||||
|
||||
deployInformation := deployapi.NewDeployInfo(&key, deployArray, passwd, isRandomPassword, true, false,
|
||||
options.HostOptions.LinuxDefaultRootUser, options.HostOptions.WindowsDefaultAdminUser,
|
||||
deployInfo.EnableCloudInit, deployInfo.LoginAccount, deployInfo.DeployTelegraf, deployInfo.TelegrafConf,
|
||||
deployInfo.Desc.UserData,
|
||||
)
|
||||
rootPath := disks[0].GetAccessPath()
|
||||
log.Debugf("deployInfo: %s", jsonutils.Marshal(deployInfo))
|
||||
deploy, err = deployclient.GetDeployClient().DeployGuestFs(ctx, &deployapi.DeployParams{
|
||||
DiskInfo: &deployapi.DiskInfo{
|
||||
Path: rootPath,
|
||||
},
|
||||
GuestDesc: &guestDesc,
|
||||
DeployInfo: deployInformation,
|
||||
VddkInfo: &vddkInfo,
|
||||
})
|
||||
if err != nil {
|
||||
model := &esxiVm{}
|
||||
dataDict.Unmarshal(model, "desc")
|
||||
logclient.AddSimpleActionLog(model, logclient.ACT_VM_DEPLOY, errors.Wrapf(err, "DeployGuestFs"), hostutils.GetComputeSession(context.Background()).GetToken(), false)
|
||||
log.Errorf("unable to DeployGuestFs: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
array := jsonutils.NewArray()
|
||||
for i := range disks {
|
||||
disk := disks[i]
|
||||
diskId := disk.GetId()
|
||||
diskDict := jsonutils.NewDict()
|
||||
diskDict.Add(jsonutils.NewString(diskId), "disk_id")
|
||||
diskDict.Add(jsonutils.NewString(disk.GetGlobalId()), "uuid")
|
||||
diskDict.Add(jsonutils.NewInt(int64(disk.GetDiskSizeMB())), "size")
|
||||
diskDict.Add(jsonutils.NewString(disk.GetAccessPath()), "path")
|
||||
diskDict.Add(jsonutils.NewString(disk.GetCacheMode()), "cache_mode")
|
||||
diskDict.Add(jsonutils.NewString(disk.GetDiskType()), "disk_type")
|
||||
diskDict.Add(jsonutils.NewString(disk.GetDriver()), "driver")
|
||||
array.Add(diskDict)
|
||||
}
|
||||
updated := jsonutils.NewDict()
|
||||
updated.Add(array, "disks")
|
||||
updated.Add(jsonutils.NewString(vm.GetGlobalId()), "uuid")
|
||||
var ret jsonutils.JSONObject
|
||||
if deploy != nil {
|
||||
ret = jsonutils.Marshal(deploy)
|
||||
} else {
|
||||
ret = jsonutils.NewDict()
|
||||
}
|
||||
ret.(*jsonutils.JSONDict).Update(updated)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (ps *SProxmoxStorage) agentCreateGuest(ctx context.Context, data jsonutils.JSONObject) (cloudprovider.ICloudVM, bool, error) {
|
||||
hd := SHostDatastore{}
|
||||
err := data.Unmarshal(&hd)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrap(err, hostutils.ParamsError.Error())
|
||||
}
|
||||
|
||||
needDeploy := true
|
||||
client, err := proxmox.NewProxmoxClientFromAccessInfo(&hd.Datastore)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrap(err, "proxmox.NewProxmoxClientFromAccessInfo")
|
||||
}
|
||||
host, err := client.GetHost(hd.HostId)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrap(err, "GetHost")
|
||||
}
|
||||
desc := api.GuestJsonDesc{}
|
||||
err = data.Unmarshal(&desc, "desc")
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrap(err, hostutils.ParamsError.Error())
|
||||
}
|
||||
createParam := cloudprovider.SManagedVMCreateConfig{
|
||||
Name: desc.Name,
|
||||
Description: desc.Description,
|
||||
ExternalImageId: desc.ExternalImageId,
|
||||
Cpu: desc.Cpu,
|
||||
MemoryMB: desc.Mem,
|
||||
SysDisk: cloudprovider.SDiskInfo{},
|
||||
DataDisks: make([]cloudprovider.SDiskInfo, 0),
|
||||
KeypairName: desc.Keypair,
|
||||
PublicKey: desc.Pubkey,
|
||||
}
|
||||
if len(desc.Nics) > 0 {
|
||||
createParam.IpAddr = desc.Nics[0].Ip
|
||||
}
|
||||
for i := range desc.Disks {
|
||||
if i == 0 {
|
||||
createParam.SysDisk = cloudprovider.SDiskInfo{
|
||||
StorageExternalId: desc.Disks[i].StorageExternalId,
|
||||
StorageType: desc.Disks[i].StorageType,
|
||||
SizeGB: desc.Disks[i].Size / 1024,
|
||||
}
|
||||
if len(desc.Disks[i].ImageInfo.ImageExternalId) > 0 {
|
||||
createParam.ExternalImageId = desc.Disks[i].ImageInfo.ImageExternalId
|
||||
}
|
||||
} else {
|
||||
createParam.DataDisks = append(createParam.DataDisks, cloudprovider.SDiskInfo{
|
||||
StorageExternalId: desc.Disks[i].StorageExternalId,
|
||||
StorageType: desc.Disks[i].StorageType,
|
||||
SizeGB: desc.Disks[i].Size / 1024,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasSuffix(createParam.ExternalImageId, ".iso") {
|
||||
needDeploy = false
|
||||
}
|
||||
|
||||
vm, err := host.CreateVM(&createParam)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrap(err, "SHost.CreateVM2")
|
||||
}
|
||||
|
||||
cloudprovider.Wait(3*time.Second, 2*time.Minute, func() (bool, error) {
|
||||
disks, err := vm.GetIDisks()
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "VM.GetIDisks")
|
||||
}
|
||||
if len(disks) > 0 {
|
||||
return true, nil
|
||||
}
|
||||
log.Debugf("wait for vm %s disks ready", vm.GetId())
|
||||
return false, nil
|
||||
})
|
||||
|
||||
return vm, needDeploy, nil
|
||||
}
|
||||
|
||||
func (ps *SProxmoxStorage) agentRebuildRoot(ctx context.Context, data jsonutils.JSONObject) (cloudprovider.ICloudVM, error) {
|
||||
type sRebuildParam struct {
|
||||
SHostDatastore
|
||||
GuestExtId string
|
||||
Desc struct {
|
||||
Disks []struct {
|
||||
SizeMb int `json:"size"`
|
||||
ImageInfo struct {
|
||||
ImageExternalId string
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
rp := sRebuildParam{}
|
||||
err := data.Unmarshal(&rp)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, hostutils.ParamsError.Error())
|
||||
}
|
||||
client, err := proxmox.NewProxmoxClientFromAccessInfo(&rp.Datastore)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "as.getHostAndDatastore")
|
||||
}
|
||||
host, err := client.GetHost(rp.HostId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetHost")
|
||||
}
|
||||
vm, err := host.GetIVMById(rp.GuestExtId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "host.GetIVMById of id '%s'", rp.GuestExtId)
|
||||
}
|
||||
sysSizeGB, imageId := 0, ""
|
||||
if len(rp.Desc.Disks) > 0 {
|
||||
sysSizeGB = rp.Desc.Disks[0].SizeMb / 1024
|
||||
imageId = rp.Desc.Disks[0].ImageInfo.ImageExternalId
|
||||
}
|
||||
_, err = vm.RebuildRoot(ctx, &cloudprovider.SManagedVMRebuildRootConfig{
|
||||
ImageId: imageId,
|
||||
SysSizeGB: sysSizeGB,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "VM.RebuildRoot")
|
||||
}
|
||||
return vm, nil
|
||||
}
|
||||
|
||||
func (ps *SProxmoxStorage) agentGetVm(ctx context.Context, data jsonutils.JSONObject) (cloudprovider.ICloudVM, error) {
|
||||
type sRebuildParam struct {
|
||||
SHostDatastore
|
||||
GuestExtId string
|
||||
}
|
||||
rp := sRebuildParam{}
|
||||
err := data.Unmarshal(&rp)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, hostutils.ParamsError.Error())
|
||||
}
|
||||
client, err := proxmox.NewProxmoxClientFromAccessInfo(&rp.Datastore)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "proxmox.NewProxmoxClientFromAccessInfo")
|
||||
}
|
||||
host, err := client.GetHost(rp.HostId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetHost")
|
||||
}
|
||||
vm, err := host.GetIVMById(rp.GuestExtId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "host.GetIVMById of id '%s'", rp.GuestExtId)
|
||||
}
|
||||
return vm, nil
|
||||
}
|
||||
@@ -282,7 +282,7 @@ func IsNetworkAvailable(
|
||||
} else {
|
||||
if !isMatchServerType(n.SNetwork) {
|
||||
return FailReason{
|
||||
Reason: fmt.Sprintf("Network %s type %s match", n.Name, n.ServerType),
|
||||
Reason: fmt.Sprintf("Network %s type %s not match %s", n.Name, n.ServerType, netTypes),
|
||||
Type: NetworkTypeMatch,
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+2
-2
@@ -2603,7 +2603,7 @@ sigs.k8s.io/structured-merge-diff/v4/value
|
||||
# sigs.k8s.io/yaml v1.3.0
|
||||
## explicit; go 1.12
|
||||
sigs.k8s.io/yaml
|
||||
# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260416100244-303b3c130516
|
||||
# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20260506084929-0e885263a936
|
||||
## explicit; go 1.24
|
||||
yunion.io/x/cloudmux/pkg/apis
|
||||
yunion.io/x/cloudmux/pkg/apis/billing
|
||||
@@ -2715,7 +2715,7 @@ yunion.io/x/log/hooks
|
||||
yunion.io/x/ovsdb/cli_util
|
||||
yunion.io/x/ovsdb/schema/ovn_nb
|
||||
yunion.io/x/ovsdb/types
|
||||
# yunion.io/x/pkg v1.10.4-0.20260224071200-5c269fa62429
|
||||
# yunion.io/x/pkg v1.10.4-0.20260422030155-01b100134978
|
||||
## explicit; go 1.18
|
||||
yunion.io/x/pkg/appctx
|
||||
yunion.io/x/pkg/errors
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ func (self *SStoragecache) IsEmulated() bool {
|
||||
}
|
||||
|
||||
func (self *SStoragecache) GetICustomizedCloudImages() ([]cloudprovider.ICloudImage, error) {
|
||||
images, err := self.region.GetImages("", ImageOwnerSelf, nil, "", "hvm", nil, "", true)
|
||||
images, err := self.region.GetImages("", ImageOwnerSelf, nil, "", "hvm", nil, "", false)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetImages")
|
||||
}
|
||||
|
||||
+17
@@ -22,3 +22,20 @@ type SVCenterAccessInfo struct {
|
||||
Password string
|
||||
PrivateId string
|
||||
}
|
||||
|
||||
type ImageCacheInput struct {
|
||||
ImageName string
|
||||
ImageId string
|
||||
HostId string
|
||||
HostIp string
|
||||
SrcHostIp string
|
||||
SrcPath string
|
||||
SrcDatastore SVCenterAccessInfo
|
||||
Datastore SVCenterAccessInfo
|
||||
Format string
|
||||
IsForce bool
|
||||
StoragecacheId string
|
||||
ImageType string
|
||||
ImageExternalId string
|
||||
StorageCacheHostIp string
|
||||
}
|
||||
|
||||
+7
-5
@@ -64,19 +64,20 @@ type SVmResource struct {
|
||||
VmId int
|
||||
Id string
|
||||
Name string
|
||||
Size int64
|
||||
Node string
|
||||
NodeId string
|
||||
Status string
|
||||
Template bool
|
||||
}
|
||||
|
||||
func (self *SRegion) GetClusterAllResources() ([]SClusterResource, error) {
|
||||
func (self *SProxmoxClient) GetClusterAllResources() ([]SClusterResource, error) {
|
||||
resources := []SClusterResource{}
|
||||
err := self.get("/cluster/resources", url.Values{}, &resources)
|
||||
return resources, err
|
||||
}
|
||||
|
||||
func (self *SRegion) GetClusterResources(resType string) ([]SClusterResource, error) {
|
||||
func (self *SProxmoxClient) GetClusterResources(resType string) ([]SClusterResource, error) {
|
||||
resources := []SClusterResource{}
|
||||
params := url.Values{}
|
||||
if len(resType) > 0 {
|
||||
@@ -86,7 +87,7 @@ func (self *SRegion) GetClusterResources(resType string) ([]SClusterResource, er
|
||||
return resources, err
|
||||
}
|
||||
|
||||
func (self *SRegion) GetClusterNodeResources() (map[string]SNodeResource, error) {
|
||||
func (self *SProxmoxClient) GetClusterNodeResources() (map[string]SNodeResource, error) {
|
||||
resources := []SClusterResource{}
|
||||
nodeResources := map[string]SNodeResource{}
|
||||
params := url.Values{}
|
||||
@@ -110,7 +111,7 @@ func (self *SRegion) GetClusterNodeResources() (map[string]SNodeResource, error)
|
||||
return nodeResources, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetClusterVmResources() (map[int]SVmResource, error) {
|
||||
func (self *SProxmoxClient) GetClusterVmResources() (map[int]SVmResource, error) {
|
||||
resources := []SClusterResource{}
|
||||
VmResources := map[int]SVmResource{}
|
||||
err := self.get("/cluster/resources", url.Values{}, &resources)
|
||||
@@ -127,6 +128,7 @@ func (self *SRegion) GetClusterVmResources() (map[int]SVmResource, error) {
|
||||
Node: res.Node,
|
||||
NodeId: fmt.Sprintf("node/%s", res.Node),
|
||||
Status: res.Status,
|
||||
Size: res.Maxdisk,
|
||||
Template: Itob(res.Template),
|
||||
}
|
||||
|
||||
@@ -137,7 +139,7 @@ func (self *SRegion) GetClusterVmResources() (map[int]SVmResource, error) {
|
||||
return VmResources, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetClusterVmMaxId() int {
|
||||
func (self *SProxmoxClient) GetClusterVmMaxId() int {
|
||||
resources := []SClusterResource{}
|
||||
idxs := []int{}
|
||||
err := self.get("/cluster/resources", url.Values{}, &resources)
|
||||
|
||||
+30
-6
@@ -37,6 +37,7 @@ type SDisk struct {
|
||||
Size int64 `json:"size"`
|
||||
Vmid string
|
||||
VolId string `json:"volid"`
|
||||
Path string `json:"path"`
|
||||
Name string `json:"name"`
|
||||
Parent string `json:"parent"`
|
||||
Content string `json:"content"`
|
||||
@@ -69,7 +70,7 @@ func (self *SDisk) CreateISnapshot(ctx context.Context, name, desc string) (clou
|
||||
}
|
||||
|
||||
func (self *SDisk) Refresh() error {
|
||||
disks, err := self.storage.zone.region.GetDisks(self.storage.Node, self.storage.Storage)
|
||||
disks, err := self.storage.cli.GetDisks(self.storage.Node, self.storage.Storage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -135,7 +136,7 @@ func (self *SDisk) Reset(ctx context.Context, snapshotId string) (string, error)
|
||||
}
|
||||
|
||||
func (self *SDisk) Resize(ctx context.Context, sizeMb int64) error {
|
||||
vm, err := self.storage.zone.region.GetInstance(self.Vmid)
|
||||
vm, err := self.storage.cli.GetInstance(self.Vmid)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetInstance")
|
||||
}
|
||||
@@ -147,7 +148,7 @@ func (self *SDisk) Resize(ctx context.Context, sizeMb int64) error {
|
||||
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 self.storage.cli.ResizeDisk(vm.Node, self.Vmid, disk.Driver, int(sizeMb-int64(self.GetDiskSizeMB()))/1024)
|
||||
}
|
||||
}
|
||||
return errors.Wrapf(cloudprovider.ErrNotFound, "%s", self.VolId)
|
||||
@@ -158,7 +159,20 @@ func (self *SDisk) GetTemplateId() string {
|
||||
}
|
||||
|
||||
func (self *SDisk) GetAccessPath() string {
|
||||
return ""
|
||||
if self.storage != nil {
|
||||
disk, err := self.storage.cli.GetDisk(self.storage.Node, self.storage.Storage, self.VolId)
|
||||
if err == nil && len(disk.Path) > 0 {
|
||||
return disk.Path
|
||||
}
|
||||
}
|
||||
if len(self.Path) > 0 {
|
||||
return self.Path
|
||||
}
|
||||
infos := strings.SplitN(self.VolId, ":", 2)
|
||||
if len(infos) == 2 && len(infos[1]) > 0 {
|
||||
return infos[1]
|
||||
}
|
||||
return self.VolId
|
||||
}
|
||||
|
||||
func (self *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) {
|
||||
@@ -173,7 +187,7 @@ func (self *SDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) {
|
||||
return []cloudprovider.ICloudSnapshot{}, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetDisks(node, storageName string) ([]SDisk, error) {
|
||||
func (self *SProxmoxClient) GetDisks(node, storageName string) ([]SDisk, error) {
|
||||
vols := []SDisk{}
|
||||
params := url.Values{}
|
||||
params.Set("content", "images")
|
||||
@@ -185,7 +199,17 @@ func (self *SRegion) GetDisks(node, storageName string) ([]SDisk, error) {
|
||||
return vols, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) ResizeDisk(node string, vmId string, driver string, sizeGb int) error {
|
||||
func (self *SProxmoxClient) GetDisk(node, storageName, volume string) (*SDisk, error) {
|
||||
vol := SDisk{}
|
||||
res := fmt.Sprintf("/nodes/%s/storage/%s/content/%s", node, storageName, url.PathEscape(volume))
|
||||
err := self.get(res, nil, &vol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &vol, nil
|
||||
}
|
||||
|
||||
func (self *SProxmoxClient) ResizeDisk(node string, vmId string, driver string, sizeGb int) error {
|
||||
body := map[string]interface{}{
|
||||
"disk": driver,
|
||||
"size": fmt.Sprintf("+%dG", sizeGb),
|
||||
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package proxmox
|
||||
|
||||
import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/cloudmux/pkg/apis/compute"
|
||||
"yunion.io/x/cloudmux/pkg/cloudprovider"
|
||||
)
|
||||
|
||||
func (self *SProxmoxClient) GetIVMById(id string) (cloudprovider.ICloudVM, error) {
|
||||
vm, err := self.GetInstance(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vm, nil
|
||||
}
|
||||
|
||||
func (cli *SProxmoxClient) GetIHosts() ([]cloudprovider.ICloudHost, error) {
|
||||
hosts, err := cli.GetHosts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := []cloudprovider.ICloudHost{}
|
||||
for i := range hosts {
|
||||
hosts[i].cli = cli
|
||||
ret = append(ret, &hosts[i])
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SProxmoxClient) GetIHostById(id string) (cloudprovider.ICloudHost, error) {
|
||||
host, err := self.GetHost(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
host.cli = self
|
||||
return host, nil
|
||||
}
|
||||
|
||||
func (self *SProxmoxClient) GetIStoragecaches() ([]cloudprovider.ICloudStoragecache, error) {
|
||||
host, err := self.GetHosts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := []cloudprovider.ICloudStoragecache{}
|
||||
for i := range host {
|
||||
ret = append(ret, &SStoragecache{
|
||||
client: self,
|
||||
Node: host[i].Node,
|
||||
})
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SProxmoxClient) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) {
|
||||
caches, err := self.GetIStoragecaches()
|
||||
if err != nil {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
for i := range caches {
|
||||
if caches[i].GetGlobalId() == id {
|
||||
return caches[i], nil
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (client *SProxmoxClient) GetIVMs() ([]cloudprovider.ICloudVM, error) {
|
||||
vms, err := client.GetInstances("")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetInstances")
|
||||
}
|
||||
ret := []cloudprovider.ICloudVM{}
|
||||
for i := range vms {
|
||||
ret = append(ret, &vms[i])
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (client *SProxmoxClient) GetIZones() ([]cloudprovider.ICloudZone, error) {
|
||||
return nil, errors.ErrNotSupported
|
||||
}
|
||||
|
||||
func (client *SProxmoxClient) GetIZoneById(id string) (cloudprovider.ICloudZone, error) {
|
||||
return nil, errors.ErrNotSupported
|
||||
}
|
||||
|
||||
func (client *SProxmoxClient) CreateIVpc(opts *cloudprovider.VpcCreateOptions) (cloudprovider.ICloudVpc, error) {
|
||||
return nil, errors.ErrNotSupported
|
||||
}
|
||||
|
||||
func (client *SProxmoxClient) GetISecurityGroupById(id string) (cloudprovider.ICloudSecurityGroup, error) {
|
||||
return nil, errors.ErrNotSupported
|
||||
}
|
||||
|
||||
func (client *SProxmoxClient) CreateISecurityGroup(conf *cloudprovider.SecurityGroupCreateInput) (cloudprovider.ICloudSecurityGroup, error) {
|
||||
return nil, errors.ErrNotSupported
|
||||
}
|
||||
|
||||
func (client *SProxmoxClient) GetCloudEnv() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (client *SProxmoxClient) GetGeographicInfo() cloudprovider.SGeographicInfo {
|
||||
return cloudprovider.SGeographicInfo{}
|
||||
}
|
||||
|
||||
func (client *SProxmoxClient) Refresh() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *SProxmoxClient) IsEmulated() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (client *SProxmoxClient) GetSysTags() map[string]string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *SProxmoxClient) GetTags() (map[string]string, error) {
|
||||
return nil, errors.Wrap(errors.ErrNotImplemented, "GetTags")
|
||||
}
|
||||
|
||||
func (client *SProxmoxClient) GetISkus() ([]cloudprovider.ICloudSku, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (client *SProxmoxClient) GetIVpcs() ([]cloudprovider.ICloudVpc, error) {
|
||||
vpc := SVpc{
|
||||
client: client,
|
||||
}
|
||||
return []cloudprovider.ICloudVpc{&vpc}, nil
|
||||
}
|
||||
|
||||
func (client *SProxmoxClient) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) {
|
||||
return nil, errors.ErrNotSupported
|
||||
}
|
||||
|
||||
func (client *SProxmoxClient) GetUUID() string {
|
||||
return client.host
|
||||
}
|
||||
|
||||
func (client *SProxmoxClient) GetId() string {
|
||||
return client.GetUUID()
|
||||
}
|
||||
|
||||
func (client *SProxmoxClient) GetName() string {
|
||||
return client.cpcfg.Name
|
||||
}
|
||||
|
||||
func (client *SProxmoxClient) GetGlobalId() string {
|
||||
return client.GetUUID()
|
||||
}
|
||||
|
||||
func (client *SProxmoxClient) GetStatus() string {
|
||||
return api.CLOUD_REGION_STATUS_INSERVER
|
||||
}
|
||||
|
||||
func (client *SProxmoxClient) GetI18n() cloudprovider.SModelI18nTable {
|
||||
table := cloudprovider.SModelI18nTable{}
|
||||
return table
|
||||
}
|
||||
|
||||
func (client *SProxmoxClient) GetProvider() string {
|
||||
return api.CLOUD_PROVIDER_PROXMOX
|
||||
}
|
||||
|
||||
func (cli *SProxmoxClient) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
|
||||
storages, err := cli.GetStorages()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := []cloudprovider.ICloudStorage{}
|
||||
for i := range storages {
|
||||
ret = append(ret, &storages[i])
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (cli *SProxmoxClient) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
|
||||
iStorages, err := cli.GetIStorages()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < len(iStorages); i += 1 {
|
||||
if iStorages[i].GetGlobalId() == id {
|
||||
return iStorages[i], nil
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
+45
-38
@@ -22,6 +22,7 @@ import (
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/cloudmux/pkg/apis"
|
||||
@@ -33,7 +34,7 @@ import (
|
||||
type SHost struct {
|
||||
multicloud.SHostBase
|
||||
ProxmoxTags
|
||||
zone *SZone
|
||||
cli *SProxmoxClient
|
||||
|
||||
Id string
|
||||
Node string
|
||||
@@ -122,7 +123,7 @@ func (self *SHost) GetAccessIp() string {
|
||||
ret := []struct {
|
||||
Address string
|
||||
}{}
|
||||
err := self.zone.region.get(network, url.Values{}, &ret)
|
||||
err := self.cli.get(network, url.Values{}, &ret)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
@@ -200,21 +201,21 @@ func (self *SHost) GetVersion() string {
|
||||
|
||||
func (self *SHost) CreateVM(opts *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) {
|
||||
|
||||
vmId := self.zone.region.GetClusterVmMaxId()
|
||||
vmId := self.cli.GetClusterVmMaxId()
|
||||
if vmId == -1 {
|
||||
return nil, errors.Errorf("failed to get vm number by %d", vmId)
|
||||
}
|
||||
vmId++
|
||||
|
||||
storage, err := self.zone.region.GetStorage(opts.SysDisk.StorageExternalId)
|
||||
storage, err := self.cli.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),
|
||||
"vmid": vmId,
|
||||
"name": opts.Name,
|
||||
//"ide2": fmt.Sprintf("%s,media=cdrom", opts.ExternalImageId),
|
||||
"ostype": "other",
|
||||
"sockets": 1,
|
||||
"cores": opts.Cpu,
|
||||
@@ -225,25 +226,36 @@ func (self *SHost) CreateVM(opts *cloudprovider.SManagedVMCreateConfig) (cloudpr
|
||||
"description": opts.OsDistribution,
|
||||
"scsihw": "virtio-scsi-pci",
|
||||
"net0": "virtio,bridge=vmbr0,firewall=1",
|
||||
"scsi0": fmt.Sprintf("%s:%d", storage.Storage, opts.SysDisk.SizeGB),
|
||||
}
|
||||
sysIndex := 0
|
||||
if strings.HasSuffix(opts.ExternalImageId, ".iso") { // iso image
|
||||
body["ide2"] = fmt.Sprintf("%s,media=cdrom", opts.ExternalImageId)
|
||||
body["scsi0"] = fmt.Sprintf("%s:%d", storage.Storage, opts.SysDisk.SizeGB)
|
||||
sysIndex = 1
|
||||
} else if strings.HasSuffix(opts.ExternalImageId, "qemu/") { // template image
|
||||
//body["ide2"] = fmt.Sprintf("%s,media=disk", opts.ExternalImageId)
|
||||
} else {
|
||||
body["scsi0"] = fmt.Sprintf("%s:0,import-from=%s,iothread=on", storage.Storage, opts.ExternalImageId)
|
||||
}
|
||||
for i, disk := range opts.DataDisks {
|
||||
storage, err := self.zone.region.GetStorage(disk.StorageExternalId)
|
||||
storage, err := self.cli.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)
|
||||
body[fmt.Sprintf("scsi%d", i+sysIndex)] = fmt.Sprintf("%s:%d", storage.Storage, opts.SysDisk.SizeGB)
|
||||
}
|
||||
|
||||
log.Debugf("opts: %s params: %s", jsonutils.Marshal(opts), jsonutils.Marshal(body))
|
||||
|
||||
res := fmt.Sprintf("/nodes/%s/qemu", self.Node)
|
||||
_, err = self.zone.region.post(res, jsonutils.Marshal(body))
|
||||
_, err = self.cli.post(res, jsonutils.Marshal(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vmIdRet := strconv.Itoa(vmId)
|
||||
cloudprovider.Wait(time.Second*5, time.Minute, func() (bool, error) {
|
||||
_, err := self.zone.region.GetInstance(vmIdRet)
|
||||
_, err := self.cli.GetInstance(vmIdRet)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == cloudprovider.ErrNotFound {
|
||||
return false, nil
|
||||
@@ -253,7 +265,7 @@ func (self *SHost) CreateVM(opts *cloudprovider.SManagedVMCreateConfig) (cloudpr
|
||||
return true, nil
|
||||
})
|
||||
|
||||
vm, err := self.zone.region.GetInstance(vmIdRet)
|
||||
vm, err := self.cli.GetInstance(vmIdRet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -263,15 +275,22 @@ func (self *SHost) CreateVM(opts *cloudprovider.SManagedVMCreateConfig) (cloudpr
|
||||
}
|
||||
|
||||
func (host *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) {
|
||||
wires, err := host.getIWires()
|
||||
wires, err := host.cli.GetWires(host.Node)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "getIWires")
|
||||
return nil, err
|
||||
}
|
||||
return cloudprovider.GetHostNetifs(host, wires), nil
|
||||
ret := []cloudprovider.ICloudHostNetInterface{}
|
||||
for i := range wires {
|
||||
nic := &SHostNic{
|
||||
wire: &wires[i],
|
||||
}
|
||||
ret = append(ret, nic)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) {
|
||||
vms, err := self.zone.region.GetInstances(self.Id)
|
||||
vms, err := self.cli.GetInstances(self.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetInstances")
|
||||
}
|
||||
@@ -284,7 +303,7 @@ func (self *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) {
|
||||
}
|
||||
|
||||
func (self *SHost) GetIVMById(id string) (cloudprovider.ICloudVM, error) {
|
||||
vm, err := self.zone.region.GetInstance(id)
|
||||
vm, err := self.cli.GetInstance(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -296,43 +315,30 @@ func (self *SHost) GetIVMById(id string) (cloudprovider.ICloudVM, error) {
|
||||
return vm, nil
|
||||
}
|
||||
|
||||
func (self *SHost) getIWires() ([]cloudprovider.ICloudWire, error) {
|
||||
wires, err := self.zone.region.GetWires()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := []cloudprovider.ICloudWire{}
|
||||
for i := range wires {
|
||||
wires[i].region = self.zone.region
|
||||
ret = append(ret, &wires[i])
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SHost) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
|
||||
storages, err := self.zone.region.GetStoragesByHost(self.Node)
|
||||
storages, err := self.cli.GetStoragesByHost(self.Node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := []cloudprovider.ICloudStorage{}
|
||||
for i := range storages {
|
||||
storages[i].zone = self.zone
|
||||
storages[i].cli = self.cli
|
||||
ret = append(ret, &storages[i])
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SHost) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
|
||||
storage, err := self.zone.region.GetStorage(id)
|
||||
storage, err := self.cli.GetStorage(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
storage.zone = self.zone
|
||||
storage.cli = self.cli
|
||||
|
||||
return storage, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetHosts() ([]SHost, error) {
|
||||
func (self *SProxmoxClient) GetHosts() ([]SHost, error) {
|
||||
hosts := []SHost{}
|
||||
resources, err := self.GetClusterNodeResources()
|
||||
if err != nil {
|
||||
@@ -348,14 +354,15 @@ func (self *SRegion) GetHosts() ([]SHost, error) {
|
||||
}
|
||||
host.Id = res.Id
|
||||
host.Node = res.Node
|
||||
host.cli = self
|
||||
hosts = append(hosts, *host)
|
||||
}
|
||||
|
||||
return hosts, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetHost(id string) (*SHost, error) {
|
||||
ret := &SHost{}
|
||||
func (self *SProxmoxClient) GetHost(id string) (*SHost, error) {
|
||||
ret := &SHost{cli: self}
|
||||
nodeName := ""
|
||||
|
||||
//"id": "node/nodeNAME",
|
||||
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package proxmox
|
||||
|
||||
import (
|
||||
"yunion.io/x/cloudmux/pkg/cloudprovider"
|
||||
"yunion.io/x/cloudmux/pkg/multicloud"
|
||||
"yunion.io/x/pkg/tristate"
|
||||
)
|
||||
|
||||
type SHostNic struct {
|
||||
multicloud.SResourceBase
|
||||
ProxmoxTags
|
||||
|
||||
client *SProxmoxClient
|
||||
|
||||
wire *SWire
|
||||
}
|
||||
|
||||
func (self *SHostNic) GetId() string {
|
||||
return self.wire.Id
|
||||
}
|
||||
|
||||
func (self *SHostNic) GetName() string {
|
||||
return self.wire.GetName()
|
||||
}
|
||||
|
||||
func (self *SHostNic) GetGlobalId() string {
|
||||
return self.wire.GetGlobalId()
|
||||
}
|
||||
|
||||
func (self *SHostNic) GetDevice() string {
|
||||
return "device"
|
||||
}
|
||||
|
||||
func (self *SHostNic) GetDriver() string {
|
||||
return "virtio"
|
||||
}
|
||||
|
||||
func (self *SHostNic) GetBridge() string {
|
||||
return self.wire.Iface
|
||||
}
|
||||
|
||||
func (self *SHostNic) GetMac() string {
|
||||
return cloudprovider.HashIdsMac(self.wire.GetGlobalId())
|
||||
}
|
||||
|
||||
func (self *SHostNic) GetVlanId() int {
|
||||
return self.wire.VlanId
|
||||
}
|
||||
|
||||
func (self *SHostNic) GetIndex() int8 {
|
||||
return int8(self.wire.Index)
|
||||
}
|
||||
|
||||
func (self *SHostNic) IsLinkUp() tristate.TriState {
|
||||
if self.wire.Active == 1 {
|
||||
return tristate.True
|
||||
}
|
||||
return tristate.False
|
||||
}
|
||||
|
||||
func (self *SHostNic) GetIpAddr() string {
|
||||
return self.wire.Address
|
||||
}
|
||||
|
||||
func (self *SHostNic) GetMtu() int32 {
|
||||
return int32(self.wire.Mtu)
|
||||
}
|
||||
|
||||
func (self *SHostNic) GetNicType() string {
|
||||
return self.wire.Type
|
||||
}
|
||||
|
||||
func (self *SHostNic) GetIWire() cloudprovider.ICloudWire {
|
||||
return self.wire
|
||||
}
|
||||
+11
-19
@@ -16,8 +16,6 @@ package proxmox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"yunion.io/x/pkg/util/imagetools"
|
||||
|
||||
@@ -33,6 +31,9 @@ type SImage struct {
|
||||
|
||||
imageInfo *imagetools.ImageInfo
|
||||
|
||||
// for template image
|
||||
Name string
|
||||
|
||||
Volid string
|
||||
Size int64
|
||||
Ctime int64
|
||||
@@ -49,6 +50,9 @@ func (self *SImage) GetId() string {
|
||||
}
|
||||
|
||||
func (self *SImage) GetName() string {
|
||||
if len(self.Name) > 0 {
|
||||
return self.Name
|
||||
}
|
||||
return self.Volid
|
||||
}
|
||||
|
||||
@@ -82,7 +86,11 @@ func (self *SImage) GetSizeByte() int64 {
|
||||
|
||||
func (img *SImage) getNormalizedImageInfo() *imagetools.ImageInfo {
|
||||
if img.imageInfo == nil {
|
||||
imgInfo := imagetools.NormalizeImageInfo(img.Volid, "", "", "", "")
|
||||
name := img.Name
|
||||
if len(name) == 0 {
|
||||
name = img.Volid
|
||||
}
|
||||
imgInfo := imagetools.NormalizeImageInfo(name, "", "", "", "")
|
||||
img.imageInfo = &imgInfo
|
||||
}
|
||||
return img.imageInfo
|
||||
@@ -126,19 +134,3 @@ func (self *SImage) GetMinOsDiskSizeGb() int {
|
||||
func (self *SImage) GetImageFormat() string {
|
||||
return self.Format
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
return images, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetImages(node, storageName string) ([]SImage, error) {
|
||||
return self.client.GetImages(node, storageName)
|
||||
}
|
||||
|
||||
+167
-36
@@ -25,6 +25,7 @@ import (
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/fileutils"
|
||||
"yunion.io/x/pkg/util/osprofile"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
@@ -93,6 +94,7 @@ type VmBase struct {
|
||||
Searchdomain string `json:"searchdomain"`
|
||||
Nameserver string `json:"nameserver"`
|
||||
Sshkeys string `json:"sshkeys"`
|
||||
Digest string `json:"digest,omitempty"`
|
||||
}
|
||||
|
||||
type SInstanceDisk struct {
|
||||
@@ -110,9 +112,10 @@ type SInstance struct {
|
||||
PowerState string
|
||||
Node string
|
||||
|
||||
VmID int `json:"vmid"`
|
||||
VmId int `json:"vmid"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"desc"`
|
||||
Digest string `json:"digest,omitempty"`
|
||||
Pool string `json:"pool,omitempty"`
|
||||
Bios string `json:"bios"`
|
||||
EFIDisk QemuDevice `json:"efidisk,omitempty"`
|
||||
@@ -139,6 +142,7 @@ type SInstance struct {
|
||||
QemuDisks map[string][]struct {
|
||||
Driver string
|
||||
DiskId string
|
||||
Size int64
|
||||
} `json:"disk"`
|
||||
QemuUnusedDisks QemuDevices `json:"unused_disk"`
|
||||
QemuVga QemuDevice `json:"vga,omitempty"`
|
||||
@@ -177,7 +181,7 @@ func (self *SInstance) GetName() string {
|
||||
}
|
||||
|
||||
func (self *SInstance) GetId() string {
|
||||
return strconv.Itoa(self.VmID)
|
||||
return strconv.Itoa(self.VmId)
|
||||
}
|
||||
|
||||
func (self *SInstance) GetGlobalId() string {
|
||||
@@ -185,8 +189,8 @@ func (self *SInstance) GetGlobalId() string {
|
||||
}
|
||||
|
||||
func (self *SInstance) Refresh() error {
|
||||
id := strconv.Itoa(int(self.VmID))
|
||||
ins, err := self.host.zone.region.GetInstance(id)
|
||||
id := strconv.Itoa(int(self.VmId))
|
||||
ins, err := self.host.cli.GetInstance(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -201,14 +205,14 @@ func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error {
|
||||
func (self *SInstance) CreateDisk(ctx context.Context, opts *cloudprovider.GuestDiskCreateOptions) (string, error) {
|
||||
body := map[string]string{}
|
||||
params := url.Values{}
|
||||
storage, err := self.host.zone.region.GetStorage(opts.StorageId)
|
||||
storage, err := self.host.cli.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))
|
||||
res := fmt.Sprintf("/nodes/%s/qemu/%d/config", self.Node, self.VmId)
|
||||
err = self.host.cli.put(res, params, jsonutils.Marshal(body))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -224,7 +228,7 @@ func (self *SInstance) CreateDisk(ctx context.Context, opts *cloudprovider.Guest
|
||||
if disks[i].Driver != driver {
|
||||
continue
|
||||
}
|
||||
volumes, err := self.host.zone.region.GetDisks(self.Node, storage.Storage)
|
||||
volumes, err := self.host.cli.GetDisks(self.Node, storage.Storage)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -240,15 +244,15 @@ func (self *SInstance) CreateDisk(ctx context.Context, opts *cloudprovider.Guest
|
||||
}
|
||||
|
||||
func (self *SInstance) ChangeConfig(ctx context.Context, opts *cloudprovider.SManagedVMChangeConfig) error {
|
||||
return self.host.zone.region.ChangeConfig(self.VmID, opts.Cpu, opts.MemoryMB)
|
||||
return self.host.cli.ChangeConfig(self.VmId, opts.Cpu, opts.MemoryMB)
|
||||
}
|
||||
|
||||
func (self *SInstance) DeleteVM(ctx context.Context) error {
|
||||
return self.host.zone.region.DeleteVM(self.VmID)
|
||||
return self.host.cli.DeleteVM(self.VmId)
|
||||
}
|
||||
|
||||
func (self *SInstance) DeployVM(ctx context.Context, opts *cloudprovider.SInstanceDeployOptions) error {
|
||||
return self.host.zone.region.ResetVmPassword(self.VmID, opts.Username, opts.Password)
|
||||
return self.host.cli.ResetVmPassword(self.VmId, opts.Username, opts.Password)
|
||||
}
|
||||
|
||||
func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error {
|
||||
@@ -267,7 +271,7 @@ func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error {
|
||||
}
|
||||
for _, disk := range disks {
|
||||
if disk.DiskId == volId {
|
||||
return self.host.zone.region.DetachDisk(self.Node, self.VmID, disk.Driver)
|
||||
return self.host.cli.DetachDisk(self.Node, self.VmId, disk.Driver)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -295,7 +299,7 @@ func (self *SInstance) GetHypervisor() string {
|
||||
}
|
||||
|
||||
func (self *SInstance) VMIdExists(vmId int) (bool, error) {
|
||||
resources, err := self.host.zone.region.GetClusterVmResources()
|
||||
resources, err := self.host.cli.GetClusterVmResources()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -306,7 +310,7 @@ func (self *SInstance) VMIdExists(vmId int) (bool, error) {
|
||||
|
||||
func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
|
||||
ret := []cloudprovider.ICloudDisk{}
|
||||
ins, err := self.host.zone.region.GetInstance(fmt.Sprintf("%d", self.VmID))
|
||||
ins, err := self.host.cli.GetInstance(fmt.Sprintf("%d", self.VmId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -318,11 +322,11 @@ func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
|
||||
}
|
||||
diskIds = append(diskIds, disks[i].DiskId)
|
||||
}
|
||||
disks, err := self.host.zone.region.GetDisks(self.host.Node, storageName)
|
||||
disks, err := self.host.cli.GetDisks(self.host.Node, storageName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetDisks")
|
||||
}
|
||||
storages, err := self.host.zone.region.GetStoragesByHost(self.Node)
|
||||
storages, err := self.host.cli.GetStoragesByHost(self.Node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -419,13 +423,13 @@ func (self *SInstance) GetProjectId() string {
|
||||
}
|
||||
|
||||
func (self *SInstance) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
|
||||
vnc, err := self.host.zone.region.GetVNCInfo(self.Node, self.VmID)
|
||||
vnc, err := self.host.cli.GetVNCInfo(self.Node, self.VmId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := &cloudprovider.ServerVncOutput{}
|
||||
ret.Protocol = "vnc"
|
||||
ret.Host = self.host.zone.region.client.host
|
||||
ret.Host = self.host.cli.host
|
||||
ret.Port = int64(vnc.Port)
|
||||
ret.Password = vnc.Ticket
|
||||
ret.Hypervisor = api.HYPERVISOR_PROXMOX
|
||||
@@ -448,8 +452,122 @@ func (self *SInstance) GetVdi() string {
|
||||
return "vnc"
|
||||
}
|
||||
|
||||
func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
|
||||
return "", cloudprovider.ErrNotSupported
|
||||
func (ins *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
|
||||
sysDriver, storageName, sysDiskId := "", "", ""
|
||||
for _storageName, disks := range ins.QemuDisks {
|
||||
for _, disk := range disks {
|
||||
if disk.Driver == "scsi0" {
|
||||
sysDriver = disk.Driver
|
||||
storageName = _storageName
|
||||
sysDiskId = disk.DiskId
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(sysDriver) > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(sysDriver) == 0 {
|
||||
return "", errors.Wrapf(cloudprovider.ErrNotFound, "sys driver not found")
|
||||
}
|
||||
storages, err := ins.host.cli.GetStoragesByHost(ins.Node)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "GetStoragesByHost")
|
||||
}
|
||||
var storage *SStorage
|
||||
for i := range storages {
|
||||
if storages[i].Storage == storageName {
|
||||
storage = &storages[i]
|
||||
}
|
||||
}
|
||||
if storage == nil {
|
||||
return "", errors.Wrapf(cloudprovider.ErrNotFound, "storage %s not found", storageName)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err = ins.host.cli.ResetBootOrder(ins.Node, ins.VmId, sysDriver)
|
||||
if err != nil {
|
||||
log.Errorf("ResetBootOrder %s%d%s %s", ins.Node, ins.VmId, sysDriver, err)
|
||||
}
|
||||
}()
|
||||
|
||||
err = ins.host.cli.DetachDisk(ins.Node, ins.VmId, sysDriver)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "DetachDisk %s", sysDriver)
|
||||
}
|
||||
|
||||
err = storage.CreateSystemDisk(ins.Node, ins.VmId, sysDriver, desc.ImageId, int(ins.DiskSize))
|
||||
if err != nil {
|
||||
ins.host.cli.AttachUnuseDisk(ins.Node, ins.VmId, sysDriver, sysDiskId)
|
||||
return "", errors.Wrapf(err, "CreateSystemDisk %s", sysDriver)
|
||||
}
|
||||
|
||||
err = ins.host.cli.RemoveUnuseDisk(ins.Node, ins.VmId)
|
||||
if err != nil {
|
||||
log.Errorf("RemoveUnuseDisk %s", err)
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (cli *SProxmoxClient) AttachUnuseDisk(node string, vmId int, driver, diskId string) error {
|
||||
vm, err := cli.GetInstance(strconv.Itoa(vmId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res := fmt.Sprintf("/nodes/%s/qemu/%d/config", node, vmId)
|
||||
err = cli.put(res, url.Values{}, jsonutils.Marshal(map[string]interface{}{
|
||||
driver: diskId,
|
||||
"digest": vm.Digest,
|
||||
}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *SProxmoxClient) ResetBootOrder(node string, vmId int, driver string) error {
|
||||
vm, err := cli.GetInstance(strconv.Itoa(vmId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res := fmt.Sprintf("/nodes/%s/qemu/%d/config", node, vmId)
|
||||
err = cli.put(res, url.Values{}, jsonutils.Marshal(map[string]interface{}{
|
||||
"boot": fmt.Sprintf("order=%s", driver),
|
||||
"digest": vm.Digest,
|
||||
}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *SProxmoxClient) RemoveUnuseDisk(node string, vmId int) error {
|
||||
res := fmt.Sprintf("/nodes/%s/qemu/%d/config", node, vmId)
|
||||
_, err := cli.post(res, jsonutils.Marshal(map[string]interface{}{
|
||||
"delete": "unused0",
|
||||
"background_delay": 5,
|
||||
}))
|
||||
return err
|
||||
}
|
||||
|
||||
func (storage *SStorage) CreateSystemDisk(node string, vmId int, driver string, imageId string, sizeGb int) error {
|
||||
vm, err := storage.cli.GetInstance(strconv.Itoa(vmId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
body := map[string]interface{}{
|
||||
driver: fmt.Sprintf("%s:%d,import-from=%s", storage.Storage, sizeGb, imageId),
|
||||
"background_delay": 5,
|
||||
"digest": vm.Digest,
|
||||
}
|
||||
res := fmt.Sprintf("/nodes/%s/qemu/%d/config", node, vmId)
|
||||
_, err = storage.cli.post(res, jsonutils.Marshal(body))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SInstance) GetSecurityGroupIds() ([]string, error) {
|
||||
@@ -461,14 +579,14 @@ func (self *SInstance) SetSecurityGroups(secgroupIds []string) error {
|
||||
}
|
||||
|
||||
func (self *SInstance) StartVM(ctx context.Context) error {
|
||||
return self.host.zone.region.StartVm(self.VmID)
|
||||
return self.host.cli.StartVm(self.VmId)
|
||||
}
|
||||
|
||||
func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error {
|
||||
if self.GetStatus() == api.VM_READY {
|
||||
return nil
|
||||
}
|
||||
return self.host.zone.region.StopVm(self.VmID)
|
||||
return self.host.cli.StopVm(self.VmId)
|
||||
}
|
||||
|
||||
func (self *SInstance) UpdateUserData(userData string) error {
|
||||
@@ -489,7 +607,7 @@ func (confMap QemuDevice) readDeviceConfig(confList []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetVmAgentNetworkInterfaces(node string, VmId int) (map[string]string, error) {
|
||||
func (self *SProxmoxClient) GetVmAgentNetworkInterfaces(node string, VmId int) (map[string]string, error) {
|
||||
intermediates := []Intermediate{}
|
||||
ipMap := map[string]string{}
|
||||
res := fmt.Sprintf("/nodes/%s/qemu/%d/agent/network-get-interfaces", node, VmId)
|
||||
@@ -509,7 +627,7 @@ func (self *SRegion) GetVmAgentNetworkInterfaces(node string, VmId int) (map[str
|
||||
return ipMap, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetVmPowerStatus(node string, VmId int) string {
|
||||
func (self *SProxmoxClient) GetVmPowerStatus(node string, VmId int) string {
|
||||
current := map[string]string{}
|
||||
res := fmt.Sprintf("/nodes/%s/qemu/%d/status/current", node, VmId)
|
||||
err := self.get(res, url.Values{}, ¤t)
|
||||
@@ -525,7 +643,7 @@ func (self *SRegion) GetVmPowerStatus(node string, VmId int) string {
|
||||
return power
|
||||
}
|
||||
|
||||
func (self *SRegion) GetQemuConfig(node string, VmId int) (*SInstance, error) {
|
||||
func (self *SProxmoxClient) GetQemuConfig(node string, VmId int) (*SInstance, error) {
|
||||
res := fmt.Sprintf("/nodes/%s/qemu/%d/config", node, VmId)
|
||||
vmConfig := map[string]interface{}{}
|
||||
vmBase := &VmBase{
|
||||
@@ -557,7 +675,8 @@ func (self *SRegion) GetQemuConfig(node string, VmId int) (*SInstance, error) {
|
||||
}
|
||||
|
||||
config := SInstance{
|
||||
VmID: int(VmId),
|
||||
VmId: int(VmId),
|
||||
Digest: vmBase.Digest,
|
||||
Name: vmBase.Name,
|
||||
Description: strings.TrimSpace(vmBase.Description),
|
||||
Tags: strings.TrimSpace(vmBase.Tags),
|
||||
@@ -584,6 +703,7 @@ func (self *SRegion) GetQemuConfig(node string, VmId int) (*SInstance, error) {
|
||||
QemuDisks: map[string][]struct {
|
||||
Driver string
|
||||
DiskId string
|
||||
Size int64
|
||||
}{},
|
||||
QemuUnusedDisks: QemuDevices{},
|
||||
QemuVga: QemuDevice{},
|
||||
@@ -632,20 +752,28 @@ func (self *SRegion) GetQemuConfig(node string, VmId int) (*SInstance, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
size := int(0)
|
||||
if sizeStr, ok := diskConfMap["size"].(string); ok {
|
||||
size, _ = fileutils.GetSizeGb(sizeStr, 'G', 1024)
|
||||
}
|
||||
|
||||
storageName, _ := ParseSubConf(diskConfMap["volume"].(string), ":")
|
||||
_, ok := config.QemuDisks[storageName]
|
||||
if !ok {
|
||||
config.QemuDisks[storageName] = []struct {
|
||||
Driver string
|
||||
DiskId string
|
||||
Size int64
|
||||
}{}
|
||||
}
|
||||
config.QemuDisks[storageName] = append(config.QemuDisks[storageName], struct {
|
||||
Driver string
|
||||
DiskId string
|
||||
Size int64
|
||||
}{
|
||||
Driver: driver,
|
||||
DiskId: diskConfMap["volume"].(string),
|
||||
Size: int64(size * 1024 * 1024 * 1024),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -801,7 +929,7 @@ func (self *SRegion) GetQemuConfig(node string, VmId int) (*SInstance, error) {
|
||||
|
||||
}
|
||||
|
||||
func (self *SRegion) GetInstances(hostId string) ([]SInstance, error) {
|
||||
func (self *SProxmoxClient) GetInstances(hostId string) ([]SInstance, error) {
|
||||
ret := []SInstance{}
|
||||
resources, err := self.GetClusterVmResources()
|
||||
if err != nil {
|
||||
@@ -809,6 +937,9 @@ func (self *SRegion) GetInstances(hostId string) ([]SInstance, error) {
|
||||
}
|
||||
|
||||
for _, res := range resources {
|
||||
if res.Template {
|
||||
continue
|
||||
}
|
||||
if res.NodeId == hostId || len(hostId) == 0 {
|
||||
instance, err := self.GetQemuConfig(res.Node, res.VmId)
|
||||
if err != nil {
|
||||
@@ -822,7 +953,7 @@ func (self *SRegion) GetInstances(hostId string) ([]SInstance, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetInstance(id string) (*SInstance, error) {
|
||||
func (self *SProxmoxClient) GetInstance(id string) (*SInstance, error) {
|
||||
resources, err := self.GetClusterVmResources()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -839,7 +970,7 @@ func (self *SRegion) GetInstance(id string) (*SInstance, error) {
|
||||
return self.GetQemuConfig(nodeName, vmId)
|
||||
}
|
||||
|
||||
func (self *SRegion) StartVm(vmId int) error {
|
||||
func (self *SProxmoxClient) StartVm(vmId int) error {
|
||||
resources, err := self.GetClusterVmResources()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -860,7 +991,7 @@ func (self *SRegion) StartVm(vmId int) error {
|
||||
|
||||
}
|
||||
|
||||
func (self *SRegion) StopVm(vmId int) error {
|
||||
func (self *SProxmoxClient) StopVm(vmId int) error {
|
||||
resources, err := self.GetClusterVmResources()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -880,7 +1011,7 @@ func (self *SRegion) StopVm(vmId int) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SRegion) DetachDisk(node string, vmId int, driver string) error {
|
||||
func (self *SProxmoxClient) DetachDisk(node string, vmId int, driver string) error {
|
||||
body := map[string]string{}
|
||||
params := url.Values{}
|
||||
body["delete"] = driver
|
||||
@@ -888,7 +1019,7 @@ func (self *SRegion) DetachDisk(node string, vmId int, driver string) error {
|
||||
return self.put(res, params, jsonutils.Marshal(body))
|
||||
}
|
||||
|
||||
func (self *SRegion) ChangeConfig(vmId int, cpu int, memMb int) error {
|
||||
func (self *SProxmoxClient) ChangeConfig(vmId int, cpu int, memMb int) error {
|
||||
vm, err := self.GetInstance(strconv.Itoa(int(vmId)))
|
||||
body := map[string]interface{}{}
|
||||
if err != nil {
|
||||
@@ -919,7 +1050,7 @@ func (self *SRegion) ChangeConfig(vmId int, cpu int, memMb int) error {
|
||||
return self.put(res, params, jsonutils.Marshal(body))
|
||||
}
|
||||
|
||||
func (self *SRegion) ResetVmPassword(vmId int, username, password string) error {
|
||||
func (self *SProxmoxClient) ResetVmPassword(vmId int, username, password string) error {
|
||||
resources, err := self.GetClusterVmResources()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -943,7 +1074,7 @@ func (self *SRegion) ResetVmPassword(vmId int, username, password string) error
|
||||
|
||||
}
|
||||
|
||||
func (self *SRegion) DeleteVM(vmId int) error {
|
||||
func (self *SProxmoxClient) DeleteVM(vmId int) error {
|
||||
id := strconv.Itoa(int(vmId))
|
||||
vm1, err := self.GetInstance(id)
|
||||
if err != nil {
|
||||
@@ -956,7 +1087,7 @@ func (self *SRegion) DeleteVM(vmId int) error {
|
||||
return self.del(res, params, nil)
|
||||
}
|
||||
|
||||
func (self *SRegion) GenVM(name, node string, cores, memMB int) (*SInstance, error) {
|
||||
func (self *SProxmoxClient) GenVM(name, node string, cores, memMB int) (*SInstance, error) {
|
||||
|
||||
vmId := self.GetClusterVmMaxId()
|
||||
if vmId == -1 {
|
||||
@@ -1000,7 +1131,7 @@ type InstanceVnc struct {
|
||||
Cert string
|
||||
}
|
||||
|
||||
func (self *SRegion) GetVNCInfo(node string, vmId int) (*InstanceVnc, error) {
|
||||
func (self *SProxmoxClient) 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{}{
|
||||
"websocket": "1",
|
||||
|
||||
-187
@@ -1,187 +0,0 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package proxmox
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
"yunion.io/x/pkg/util/rbacscope"
|
||||
|
||||
api "yunion.io/x/cloudmux/pkg/apis/compute"
|
||||
"yunion.io/x/cloudmux/pkg/cloudprovider"
|
||||
"yunion.io/x/cloudmux/pkg/multicloud"
|
||||
)
|
||||
|
||||
type SNetwork struct {
|
||||
multicloud.SNetworkBase
|
||||
ProxmoxTags
|
||||
|
||||
wire *SWire
|
||||
|
||||
Id string
|
||||
|
||||
Priority int `json:"priority"`
|
||||
Families []string `json:"families"`
|
||||
Method string `json:"method"`
|
||||
Autostart int `json:"autostart,omitempty"`
|
||||
Iface string `json:"iface"`
|
||||
BridgeFd string `json:"bridge_fd,omitempty"`
|
||||
Method6 string `json:"method6"`
|
||||
BridgePorts string `json:"bridge_ports,omitempty"`
|
||||
Type string `json:"type"`
|
||||
Active int `json:"active"`
|
||||
BridgeStp string `json:"bridge_stp,omitempty"`
|
||||
BridgeVids string `json:"bridge_vids,omitempty"`
|
||||
Netmask string `json:"netmask,omitempty"`
|
||||
Address string `json:"address,omitempty"`
|
||||
BridgeVlanAware int `json:"bridge_vlan_aware,omitempty"`
|
||||
Gateway string `json:"gateway,omitempty"`
|
||||
Cidr string `json:"cidr,omitempty"`
|
||||
Exists int `json:"exists,omitempty"`
|
||||
Options []string `json:"options,omitempty"`
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetName() string {
|
||||
return self.Iface
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetId() string {
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetGlobalId() string {
|
||||
return self.GetId()
|
||||
}
|
||||
|
||||
func (self *SNetwork) Refresh() error {
|
||||
ret, err := self.wire.region.GetNetwork(self.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonutils.Update(self, ret)
|
||||
}
|
||||
|
||||
func (self *SNetwork) Delete() error {
|
||||
return cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetAllocTimeoutSeconds() int {
|
||||
return 120 // 2 minutes
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetGateway() string {
|
||||
return self.Gateway
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetIWire() cloudprovider.ICloudWire {
|
||||
return self.wire
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetIpStart() string {
|
||||
pref, _ := netutils.NewIPV4Prefix(self.Cidr)
|
||||
startIp := pref.Address.NetAddr(pref.MaskLen) // 0
|
||||
startIp = startIp.StepUp() // 1
|
||||
startIp = startIp.StepUp() // 2
|
||||
return startIp.String()
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetIpEnd() string {
|
||||
pref, _ := netutils.NewIPV4Prefix(self.Cidr)
|
||||
endIp := pref.Address.BroadcastAddr(pref.MaskLen) // 255
|
||||
endIp = endIp.StepDown() // 254
|
||||
endIp = endIp.StepDown() // 253
|
||||
endIp = endIp.StepDown() // 252
|
||||
return endIp.String()
|
||||
}
|
||||
|
||||
func (self *SNetwork) Contains(_ip string) bool {
|
||||
start, _ := netutils.NewIPV4Addr(self.GetIpStart())
|
||||
end, _ := netutils.NewIPV4Addr(self.GetIpEnd())
|
||||
ip, _ := netutils.NewIPV4Addr(_ip)
|
||||
return netutils.NewIPV4AddrRange(start, end).Contains(ip)
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetIpMask() int8 {
|
||||
pref, _ := netutils.NewIPV4Prefix(self.Cidr)
|
||||
return pref.MaskLen
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetProjectId() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetPublicScope() rbacscope.TRbacScope {
|
||||
return rbacscope.ScopeDomain
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetServerType() string {
|
||||
return api.NETWORK_TYPE_GUEST
|
||||
}
|
||||
|
||||
func (self *SNetwork) GetStatus() string {
|
||||
return api.NETWORK_STATUS_AVAILABLE
|
||||
}
|
||||
|
||||
func (self *SRegion) GetNetworks() ([]SNetwork, error) {
|
||||
ret := []SNetwork{}
|
||||
|
||||
resources, err := self.GetClusterNodeResources()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, res := range resources {
|
||||
networks := []SNetwork{}
|
||||
urlNetwork := fmt.Sprintf("/nodes/%s/network", res.Node)
|
||||
self.get(urlNetwork, url.Values{}, &networks)
|
||||
|
||||
for _, iface := range networks {
|
||||
id := fmt.Sprintf("network/%s/%s", res.Node, iface.Iface)
|
||||
iface.Id = id
|
||||
|
||||
ret = append(ret, iface)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetNetwork(id string) (*SNetwork, error) {
|
||||
ret := &SNetwork{}
|
||||
|
||||
//"id": "network/nodeNAME/iface",
|
||||
splited := strings.Split(id, "/")
|
||||
nodeName := ""
|
||||
networkName := ""
|
||||
|
||||
if len(splited) == 3 {
|
||||
nodeName = splited[1]
|
||||
networkName = splited[2]
|
||||
} else {
|
||||
return nil, errors.Errorf("failed to get network by %s", id)
|
||||
}
|
||||
|
||||
res := fmt.Sprintf("/nodes/%s/network/%s", nodeName, networkName)
|
||||
err := self.get(res, url.Values{}, ret)
|
||||
ret.Id = id
|
||||
|
||||
return ret, err
|
||||
}
|
||||
+1
-1
@@ -33,7 +33,7 @@ type SInstanceNic struct {
|
||||
}
|
||||
|
||||
func (self *SInstanceNic) GetId() string {
|
||||
return fmt.Sprintf("%d/%s", self.ins.VmID, self.NicId)
|
||||
return fmt.Sprintf("%d/%s", self.ins.VmId, self.NicId)
|
||||
}
|
||||
|
||||
func (self *SInstanceNic) GetIP() string {
|
||||
|
||||
+8
-16
@@ -31,7 +31,7 @@ import (
|
||||
)
|
||||
|
||||
type SProxmoxProviderFactory struct {
|
||||
cloudprovider.SPrivateCloudBaseProviderFactory
|
||||
cloudprovider.SPremiseBaseProviderFactory
|
||||
}
|
||||
|
||||
func (self *SProxmoxProviderFactory) GetId() string {
|
||||
@@ -143,10 +143,6 @@ type SProxmoxProvider struct {
|
||||
client *proxmox.SProxmoxClient
|
||||
}
|
||||
|
||||
func (self *SProxmoxProvider) GetCloudRegionExternalIdPrefix() string {
|
||||
return self.client.GetCloudRegionExternalIdPrefix()
|
||||
}
|
||||
|
||||
func (self *SProxmoxProvider) GetSysInfo() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.NewDict(), nil
|
||||
}
|
||||
@@ -164,20 +160,11 @@ func (self *SProxmoxProvider) GetAccountId() string {
|
||||
}
|
||||
|
||||
func (self *SProxmoxProvider) GetIRegions() ([]cloudprovider.ICloudRegion, error) {
|
||||
return self.client.GetIRegions()
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SProxmoxProvider) GetIRegionById(id string) (cloudprovider.ICloudRegion, error) {
|
||||
regions, err := self.GetIRegions()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range regions {
|
||||
if regions[i].GetGlobalId() == id {
|
||||
return regions[i], nil
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SProxmoxProvider) GetBalance() (*cloudprovider.SBalanceInfo, error) {
|
||||
@@ -188,6 +175,10 @@ func (self *SProxmoxProvider) GetBalance() (*cloudprovider.SBalanceInfo, error)
|
||||
}, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SProxmoxProvider) GetOnPremiseIRegion() (cloudprovider.ICloudRegion, error) {
|
||||
return self.client, nil
|
||||
}
|
||||
|
||||
func (self *SProxmoxProvider) GetIProjects() ([]cloudprovider.ICloudProject, error) {
|
||||
return []cloudprovider.ICloudProject{}, nil
|
||||
}
|
||||
@@ -207,3 +198,4 @@ func (self *SProxmoxProvider) GetObjectCannedAcls(regionId string) []string {
|
||||
func (self *SProxmoxProvider) GetCapabilities() []string {
|
||||
return self.client.GetCapabilities()
|
||||
}
|
||||
|
||||
|
||||
+49
-53
@@ -25,15 +25,17 @@ import (
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/httputils"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
api "yunion.io/x/cloudmux/pkg/apis/compute"
|
||||
"yunion.io/x/cloudmux/pkg/cloudprovider"
|
||||
"yunion.io/x/cloudmux/pkg/multicloud"
|
||||
"yunion.io/x/cloudmux/pkg/multicloud/esxi/vcenter"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -42,6 +44,11 @@ const (
|
||||
)
|
||||
|
||||
type SProxmoxClient struct {
|
||||
multicloud.SRegion
|
||||
multicloud.SRegionEipBase
|
||||
multicloud.SNoObjectStorageRegion
|
||||
multicloud.SRegionLbBase
|
||||
|
||||
*ProxmoxClientConfig
|
||||
}
|
||||
|
||||
@@ -81,6 +88,27 @@ func (self *ProxmoxClientConfig) CloudproviderConfig(cpcfg cloudprovider.Provide
|
||||
return self
|
||||
}
|
||||
|
||||
func NewProxmoxClientFromAccessInfo(accessInfo *vcenter.SVCenterAccessInfo) (*SProxmoxClient, error) {
|
||||
if len(accessInfo.VcenterId) > 0 {
|
||||
tmp, err := utils.DescryptAESBase64(accessInfo.VcenterId, accessInfo.Password)
|
||||
if err == nil {
|
||||
accessInfo.Password = tmp
|
||||
}
|
||||
}
|
||||
client, err := NewProxmoxClient(
|
||||
NewProxmoxClientConfig(
|
||||
accessInfo.Account,
|
||||
accessInfo.Password,
|
||||
accessInfo.Host,
|
||||
accessInfo.Port,
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func NewProxmoxClient(cfg *ProxmoxClientConfig) (*SProxmoxClient, error) {
|
||||
|
||||
client := &SProxmoxClient{
|
||||
@@ -120,22 +148,11 @@ func (self *SProxmoxClient) auth() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SProxmoxClient) GetRegion() *SRegion {
|
||||
region := &SRegion{client: self}
|
||||
return region
|
||||
}
|
||||
|
||||
func (self *SProxmoxClient) GetRegions() ([]SRegion, error) {
|
||||
ret := []SRegion{}
|
||||
ret = append(ret, SRegion{client: self})
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
type ProxmoxError struct {
|
||||
Url string
|
||||
params jsonutils.JSONObject
|
||||
Message string
|
||||
Code int
|
||||
Params []string
|
||||
Errors string
|
||||
Status string
|
||||
}
|
||||
@@ -148,7 +165,7 @@ func (ce *ProxmoxError) ParseErrorFromJsonResponse(statusCode int, status string
|
||||
ce.Status = status
|
||||
if body != nil {
|
||||
body.Unmarshal(ce)
|
||||
log.Errorf("%s status: %s(%d) error: %v", ce.Url, status, statusCode, body.PrettyString())
|
||||
log.Errorf("%s status: %s(%d) params: %s error: %s", ce.Url, status, statusCode, jsonutils.Marshal(ce.params).String(), body.String())
|
||||
}
|
||||
if ce.Code == 0 && statusCode > 0 {
|
||||
ce.Code = statusCode
|
||||
@@ -289,7 +306,7 @@ func (cli *SProxmoxClient) __jsonRequest(method httputils.THttpMethod, res strin
|
||||
}
|
||||
|
||||
req.SetHeader(header)
|
||||
oe := &ProxmoxError{Url: url}
|
||||
oe := &ProxmoxError{Url: url, params: jsonutils.Marshal(params)}
|
||||
_, resp, err := client.Send(context.Background(), req, oe, cli.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -298,9 +315,9 @@ func (cli *SProxmoxClient) __jsonRequest(method httputils.THttpMethod, res strin
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (cli *SProxmoxClient) upload(node, storageName, filename string, reader io.Reader) (*SImage, error) {
|
||||
if !strings.HasSuffix(filename, ".iso") {
|
||||
filename = filename + ".iso"
|
||||
func (cli *SProxmoxClient) upload(node, storageName, filename, format string, reader io.Reader) error {
|
||||
if !strings.HasSuffix(filename, format) {
|
||||
filename = filename + "." + format
|
||||
}
|
||||
filename = filepath.Base(filename)
|
||||
client := cli.getDefaultClient()
|
||||
@@ -309,22 +326,26 @@ func (cli *SProxmoxClient) upload(node, storageName, filename string, reader io.
|
||||
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
err := writer.WriteField("content", "iso")
|
||||
content := "iso"
|
||||
if format != "iso" {
|
||||
content = "import"
|
||||
}
|
||||
err := writer.WriteField("content", content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return errors.Wrapf(err, "WriteField")
|
||||
}
|
||||
part, err := writer.CreateFormFile("filename", filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return errors.Wrapf(err, "CreateFormFile")
|
||||
}
|
||||
_, err = io.Copy(part, reader)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "io.Copy")
|
||||
return errors.Wrapf(err, "io.Copy")
|
||||
}
|
||||
writer.Close()
|
||||
req, err := http.NewRequest("POST", url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return errors.Wrapf(err, "NewRequest")
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
@@ -336,40 +357,22 @@ func (cli *SProxmoxClient) upload(node, storageName, filename string, reader io.
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return errors.Wrapf(err, "Do")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return errors.Wrapf(err, "ReadAll")
|
||||
}
|
||||
obj, err := jsonutils.Parse(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return errors.Wrapf(err, "Parse")
|
||||
}
|
||||
if obj.Contains("errors") {
|
||||
return nil, fmt.Errorf("%s", string(data))
|
||||
return errors.Wrapf(fmt.Errorf("%s", string(data)), "errors")
|
||||
}
|
||||
|
||||
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 (cli *SProxmoxClient) GetCloudRegionExternalIdPrefix() string {
|
||||
return fmt.Sprintf("%s/%s", CLOUD_PROVIDER_PROXMOX, cli.cpcfg.Id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SProxmoxClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) {
|
||||
@@ -385,13 +388,6 @@ func (self *SProxmoxClient) GetAccountId() string {
|
||||
return self.host
|
||||
}
|
||||
|
||||
func (self *SProxmoxClient) GetIRegions() ([]cloudprovider.ICloudRegion, error) {
|
||||
ret := []cloudprovider.ICloudRegion{}
|
||||
region := self.GetRegion()
|
||||
ret = append(ret, region)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SProxmoxClient) GetCapabilities() []string {
|
||||
ret := []string{
|
||||
cloudprovider.CLOUD_CAPABILITY_COMPUTE,
|
||||
|
||||
-229
@@ -1,229 +0,0 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package proxmox
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type SRegion struct {
|
||||
multicloud.SRegion
|
||||
multicloud.SRegionEipBase
|
||||
multicloud.SNoObjectStorageRegion
|
||||
multicloud.SRegionLbBase
|
||||
|
||||
client *SProxmoxClient
|
||||
}
|
||||
|
||||
func (self *SRegion) GetClient() *SProxmoxClient {
|
||||
return self.client
|
||||
}
|
||||
|
||||
func (self *SRegion) GetName() string {
|
||||
return self.client.cpcfg.Name
|
||||
}
|
||||
|
||||
func (self *SRegion) GetI18n() cloudprovider.SModelI18nTable {
|
||||
table := cloudprovider.SModelI18nTable{}
|
||||
table["name"] = cloudprovider.NewSModelI18nEntry(self.GetName()).CN(self.GetName())
|
||||
return table
|
||||
}
|
||||
|
||||
func (self *SRegion) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", CLOUD_PROVIDER_PROXMOX, self.client.cpcfg.Id)
|
||||
}
|
||||
|
||||
func (self *SRegion) GetGlobalId() string {
|
||||
return self.GetId()
|
||||
}
|
||||
|
||||
func (self *SRegion) GetProvider() string {
|
||||
return CLOUD_PROVIDER_PROXMOX
|
||||
}
|
||||
|
||||
func (self *SRegion) GetCloudEnv() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SRegion) GetGeographicInfo() cloudprovider.SGeographicInfo {
|
||||
return cloudprovider.SGeographicInfo{}
|
||||
}
|
||||
|
||||
func (self *SRegion) GetStatus() string {
|
||||
return api.CLOUD_REGION_STATUS_INSERVER
|
||||
}
|
||||
|
||||
func (self *SRegion) Refresh() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetISecurityGroupById(secgroupId string) (cloudprovider.ICloudSecurityGroup, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateISecurityGroup(conf *cloudprovider.SecurityGroupCreateInput) (cloudprovider.ICloudSecurityGroup, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SRegion) CreateIVpc(conf *cloudprovider.VpcCreateOptions) (cloudprovider.ICloudVpc, error) {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SRegion) GetCapabilities() []string {
|
||||
return self.client.GetCapabilities()
|
||||
}
|
||||
|
||||
func (self *SRegion) getVpc() *SVpc {
|
||||
return &SVpc{region: self}
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIVpcs() ([]cloudprovider.ICloudVpc, error) {
|
||||
vpc := self.getVpc()
|
||||
return []cloudprovider.ICloudVpc{vpc}, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) {
|
||||
vpcs, err := self.GetIVpcs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range vpcs {
|
||||
if vpcs[i].GetGlobalId() == id {
|
||||
return vpcs[i], nil
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIZoneById(id string) (cloudprovider.ICloudZone, error) {
|
||||
zone, err := self.GetZone()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetZone()")
|
||||
}
|
||||
return zone, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIZones() ([]cloudprovider.ICloudZone, error) {
|
||||
zone, err := self.GetZone()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := []cloudprovider.ICloudZone{}
|
||||
zone.region = self
|
||||
ret = append(ret, zone)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIVMById(id string) (cloudprovider.ICloudVM, error) {
|
||||
vm, err := self.GetInstance(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vm, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIHostById(id string) (cloudprovider.ICloudHost, error) {
|
||||
host, err := self.GetHost(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
zone, err := self.GetZone()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
host.zone = zone
|
||||
return host, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIStoragecaches() ([]cloudprovider.ICloudStoragecache, error) {
|
||||
storages, err := self.GetStorages()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := []cloudprovider.ICloudStoragecache{}
|
||||
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
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) {
|
||||
caches, err := self.GetIStoragecaches()
|
||||
if err != nil {
|
||||
return nil, cloudprovider.ErrNotSupported
|
||||
}
|
||||
for i := range caches {
|
||||
if caches[i].GetGlobalId() == id {
|
||||
return caches[i], nil
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SRegion) get(res string, params url.Values, retVal interface{}) error {
|
||||
return self.client.get(res, params, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) getAgent(res string, params url.Values, retVal interface{}) error {
|
||||
return self.client.getAgent(res, params, retVal)
|
||||
}
|
||||
|
||||
func (self *SRegion) post(res string, params interface{}) (jsonutils.JSONObject, error) {
|
||||
return self.client.post(res, params)
|
||||
}
|
||||
|
||||
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 {
|
||||
return self.client.del(res, params, retVal)
|
||||
}
|
||||
|
||||
func (region *SRegion) GetIVMs() ([]cloudprovider.ICloudVM, error) {
|
||||
vms, err := region.GetInstances("")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetInstances")
|
||||
}
|
||||
ret := []cloudprovider.ICloudVM{}
|
||||
for i := range vms {
|
||||
ret = append(ret, &vms[i])
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
+68
-23
@@ -16,6 +16,8 @@ package proxmox
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
@@ -30,7 +32,7 @@ type SStorage struct {
|
||||
multicloud.SStorageBase
|
||||
ProxmoxTags
|
||||
|
||||
zone *SZone
|
||||
cli *SProxmoxClient
|
||||
|
||||
Storage string `json:"storage"`
|
||||
Status string
|
||||
@@ -63,7 +65,7 @@ func (self *SStorage) GetGlobalId() string {
|
||||
}
|
||||
|
||||
func (self *SStorage) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
|
||||
disks, err := self.zone.region.GetDisks(self.Node, self.Storage)
|
||||
disks, err := self.cli.GetDisks(self.Node, self.Storage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -109,9 +111,8 @@ func (self *SStorage) GetIDiskById(id string) (cloudprovider.ICloudDisk, error)
|
||||
|
||||
func (self *SStorage) GetIStoragecache() cloudprovider.ICloudStoragecache {
|
||||
return &SStoragecache{
|
||||
region: self.zone.region,
|
||||
Node: self.Node,
|
||||
isShare: self.Shared == 1,
|
||||
client: self.cli,
|
||||
Node: self.Node,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +132,7 @@ func (self *SStorage) GetStatus() string {
|
||||
}
|
||||
|
||||
func (self *SStorage) Refresh() error {
|
||||
ret, err := self.zone.region.GetStorage(self.GetGlobalId())
|
||||
ret, err := self.cli.GetStorage(self.GetGlobalId())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -139,7 +140,7 @@ func (self *SStorage) Refresh() error {
|
||||
}
|
||||
|
||||
func (self *SStorage) GetIZone() cloudprovider.ICloudZone {
|
||||
return self.zone
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SStorage) GetStorageConf() jsonutils.JSONObject {
|
||||
@@ -154,20 +155,7 @@ func (self *SStorage) IsSysDiskStore() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SRegion) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
|
||||
storage, err := self.GetStorage(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
zone, err := self.GetZone()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
storage.zone = zone
|
||||
return storage, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetStorages() ([]SStorage, error) {
|
||||
func (self *SProxmoxClient) GetStorages() ([]SStorage, error) {
|
||||
storages := []SStorage{}
|
||||
resources, err := self.GetClusterResources("storage")
|
||||
if err != nil {
|
||||
@@ -182,6 +170,7 @@ func (self *SRegion) GetStorages() ([]SStorage, error) {
|
||||
storageMap := map[string]bool{}
|
||||
ret := []SStorage{}
|
||||
for i := range storages {
|
||||
storages[i].cli = self
|
||||
if storages[i].Shared == 0 {
|
||||
ret = append(ret, storages[i])
|
||||
continue
|
||||
@@ -195,7 +184,7 @@ func (self *SRegion) GetStorages() ([]SStorage, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetStoragesByHost(node string) ([]SStorage, error) {
|
||||
func (self *SProxmoxClient) GetStoragesByHost(node string) ([]SStorage, error) {
|
||||
storages, err := self.GetStorages()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -209,7 +198,63 @@ func (self *SRegion) GetStoragesByHost(node string) ([]SStorage, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetStorage(id string) (*SStorage, error) {
|
||||
func (self *SProxmoxClient) GetImages(host, storage, content string) ([]SImage, error) {
|
||||
images := []SImage{}
|
||||
params := url.Values{}
|
||||
params.Add("content", content)
|
||||
path := fmt.Sprintf("/nodes/%s/storage/%s/content", host, storage)
|
||||
err := self.get(path, params, &images)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return images, nil
|
||||
}
|
||||
|
||||
func (self *SStorage) GetImages() ([]SImage, error) {
|
||||
ret := []SImage{}
|
||||
for _, content := range []string{"iso", "import"} {
|
||||
if !strings.Contains(self.Content, content) {
|
||||
continue
|
||||
}
|
||||
images, err := self.cli.GetImages(self.Node, self.Storage, content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret = append(ret, images...)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SStorage) SearchImage(content, imageId, imageName, format string) (*SImage, error) {
|
||||
files, err := self.cli.GetImages(self.Node, self.Storage, content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name1 := fmt.Sprintf("%s:%s/%s", self.Storage, content, imageName)
|
||||
if !strings.HasSuffix(name1, format) {
|
||||
name1 = fmt.Sprintf("%s.%s", name1, format)
|
||||
}
|
||||
name2 := fmt.Sprintf("%s:%s/%s", self.Storage, content, imageId)
|
||||
if !strings.HasSuffix(name2, format) {
|
||||
name2 = fmt.Sprintf("%s.%s", name2, format)
|
||||
}
|
||||
for i := range files {
|
||||
if files[i].Volid == name1 || files[i].Volid == name2 {
|
||||
return &files[i], nil
|
||||
}
|
||||
}
|
||||
return nil, cloudprovider.ErrNotFound
|
||||
}
|
||||
|
||||
func (self *SProxmoxClient) ImportImage(host, storage, imagId, format string, reader io.Reader) error {
|
||||
err := self.upload(host, storage, imagId, format, reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SProxmoxClient) GetStorage(id string) (*SStorage, error) {
|
||||
storages, err := self.GetStorages()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
+30
-60
@@ -17,68 +17,67 @@ 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
|
||||
|
||||
region *SRegion
|
||||
Node string
|
||||
isShare bool
|
||||
client *SProxmoxClient
|
||||
Node string
|
||||
}
|
||||
|
||||
func (self *SStoragecache) GetGlobalId() string {
|
||||
if self.isShare {
|
||||
return fmt.Sprintf("%s-share", self.region.GetGlobalId())
|
||||
}
|
||||
return fmt.Sprintf("%s-%s", self.region.GetGlobalId(), self.Node)
|
||||
return fmt.Sprintf("%s-%s", self.client.GetGlobalId(), self.Node)
|
||||
}
|
||||
|
||||
func (self *SStoragecache) GetId() string {
|
||||
return self.region.GetId()
|
||||
return self.client.GetId()
|
||||
}
|
||||
|
||||
func (self *SStoragecache) GetName() string {
|
||||
if self.isShare {
|
||||
return fmt.Sprintf("%s-share", self.region.GetName())
|
||||
}
|
||||
return fmt.Sprintf("%s-%s", self.region.GetName(), self.Node)
|
||||
return fmt.Sprintf("%s-%s", self.client.GetName(), self.Node)
|
||||
}
|
||||
|
||||
func (self *SStoragecache) GetStatus() string {
|
||||
return "available"
|
||||
}
|
||||
|
||||
func (self *SStoragecache) GetICloudImages() ([]cloudprovider.ICloudImage, error) {
|
||||
ret := []cloudprovider.ICloudImage{}
|
||||
storages, err := self.region.GetStorages()
|
||||
func (self *SProxmoxClient) GetTemplateImages(node string) ([]SImage, error) {
|
||||
ret := []SImage{}
|
||||
vms, err := self.GetClusterVmResources()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range storages {
|
||||
if !strings.Contains(storages[i].Content, "iso") {
|
||||
for i := range vms {
|
||||
if !vms[i].Template {
|
||||
continue
|
||||
}
|
||||
if (self.isShare && storages[i].Shared != 1) || (!self.isShare && storages[i].Node != self.Node) {
|
||||
if len(node) > 0 && vms[i].Node != 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])
|
||||
image := SImage{
|
||||
Name: vms[i].Name,
|
||||
Volid: vms[i].Id,
|
||||
Size: vms[i].Size,
|
||||
}
|
||||
ret = append(ret, image)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SStoragecache) GetICloudImages() ([]cloudprovider.ICloudImage, error) {
|
||||
ret := []cloudprovider.ICloudImage{}
|
||||
images, err := self.client.GetTemplateImages(self.Node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range images {
|
||||
images[i].cache = self
|
||||
ret = append(ret, &images[i])
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
@@ -105,34 +104,5 @@ func (self *SStoragecache) GetPath() string {
|
||||
}
|
||||
|
||||
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 *SRegion) UploadImage(node, storage, filename string, reader io.Reader) (*SImage, error) {
|
||||
return self.client.upload(node, storage, filename, reader)
|
||||
return "", cloudprovider.ErrNotSupported
|
||||
}
|
||||
|
||||
+16
-10
@@ -24,15 +24,15 @@ type SVpc struct {
|
||||
multicloud.SVpc
|
||||
ProxmoxTags
|
||||
|
||||
region *SRegion
|
||||
client *SProxmoxClient
|
||||
}
|
||||
|
||||
func (self *SVpc) GetName() string {
|
||||
return "Default"
|
||||
return self.client.GetName()
|
||||
}
|
||||
|
||||
func (self *SVpc) GetId() string {
|
||||
return self.region.GetId()
|
||||
return self.client.GetId()
|
||||
}
|
||||
|
||||
func (self *SVpc) GetGlobalId() string {
|
||||
@@ -64,24 +64,30 @@ func (self *SVpc) GetISecurityGroups() ([]cloudprovider.ICloudSecurityGroup, err
|
||||
}
|
||||
|
||||
func (self *SVpc) GetIWires() ([]cloudprovider.ICloudWire, error) {
|
||||
wires, err := self.region.GetWires()
|
||||
nodes, err := self.client.GetHosts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := []cloudprovider.ICloudWire{}
|
||||
for i := range wires {
|
||||
wires[i].region = self.region
|
||||
ret = append(ret, &wires[i])
|
||||
for i := range nodes {
|
||||
wires, err := self.client.GetWires(nodes[i].Node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for j := range wires {
|
||||
wires[j].client = self.client
|
||||
ret = append(ret, &wires[j])
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SVpc) GetIWireById(id string) (cloudprovider.ICloudWire, error) {
|
||||
wire, err := self.region.GetWire()
|
||||
wire, err := self.client.GetWire(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
wire.region = self.region
|
||||
wire.client = self.client
|
||||
return wire, nil
|
||||
}
|
||||
|
||||
@@ -90,7 +96,7 @@ func (self *SVpc) GetIsDefault() bool {
|
||||
}
|
||||
|
||||
func (self *SVpc) GetRegion() cloudprovider.ICloudRegion {
|
||||
return self.region
|
||||
return self.client
|
||||
}
|
||||
|
||||
func (self *SVpc) GetStatus() string {
|
||||
|
||||
+77
-28
@@ -15,28 +15,59 @@
|
||||
package proxmox
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
api "yunion.io/x/cloudmux/pkg/apis/compute"
|
||||
"yunion.io/x/cloudmux/pkg/cloudprovider"
|
||||
"yunion.io/x/cloudmux/pkg/multicloud"
|
||||
"yunion.io/x/pkg/errors"
|
||||
)
|
||||
|
||||
type SWire struct {
|
||||
multicloud.SResourceBase
|
||||
ProxmoxTags
|
||||
|
||||
region *SRegion
|
||||
client *SProxmoxClient
|
||||
|
||||
Id string
|
||||
Node string
|
||||
Index int8
|
||||
|
||||
Priority int `json:"priority"`
|
||||
Families []string `json:"families"`
|
||||
Method string `json:"method"`
|
||||
Autostart int `json:"autostart,omitempty"`
|
||||
Iface string `json:"iface"`
|
||||
BridgeFd string `json:"bridge_fd,omitempty"`
|
||||
Method6 string `json:"method6"`
|
||||
BridgePorts string `json:"bridge_ports,omitempty"`
|
||||
Type string `json:"type"`
|
||||
Active int `json:"active"`
|
||||
Mtu int `json:"mtu,omitempty"`
|
||||
BridgeStp string `json:"bridge_stp,omitempty"`
|
||||
BridgeVids string `json:"bridge_vids,omitempty"`
|
||||
Netmask string `json:"netmask,omitempty"`
|
||||
Address string `json:"address,omitempty"`
|
||||
BridgeVlanAware int `json:"bridge_vlan_aware,omitempty"`
|
||||
Gateway string `json:"gateway,omitempty"`
|
||||
Cidr string `json:"cidr,omitempty"`
|
||||
Exists int `json:"exists,omitempty"`
|
||||
Options []string `json:"options,omitempty"`
|
||||
VlanId int `json:"vlan-id,omitempty"`
|
||||
}
|
||||
|
||||
func (self *SWire) GetId() string {
|
||||
return self.region.GetId()
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SWire) GetName() string {
|
||||
return "Default"
|
||||
return fmt.Sprintf("%s-%s", self.Node, self.Iface)
|
||||
}
|
||||
|
||||
func (self *SWire) GetGlobalId() string {
|
||||
return self.GetId()
|
||||
return self.Id
|
||||
}
|
||||
|
||||
func (self *SWire) CreateINetwork(opts *cloudprovider.SNetworkCreateOptions) (cloudprovider.ICloudNetwork, error) {
|
||||
@@ -48,48 +79,66 @@ func (self *SWire) GetBandwidth() int {
|
||||
}
|
||||
|
||||
func (self *SWire) GetINetworks() ([]cloudprovider.ICloudNetwork, error) {
|
||||
nets, err := self.region.GetNetworks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := []cloudprovider.ICloudNetwork{}
|
||||
for i := range nets {
|
||||
nets[i].wire = self
|
||||
ret = append(ret, &nets[i])
|
||||
}
|
||||
return ret, nil
|
||||
return nil, errors.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SWire) GetINetworkById(id string) (cloudprovider.ICloudNetwork, error) {
|
||||
net, err := self.region.GetNetwork(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
net.wire = self
|
||||
return net, nil
|
||||
return nil, errors.ErrNotSupported
|
||||
}
|
||||
|
||||
func (self *SWire) GetIVpc() cloudprovider.ICloudVpc {
|
||||
return self.region.getVpc()
|
||||
return &SVpc{
|
||||
client: self.client,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SWire) GetIZone() cloudprovider.ICloudZone {
|
||||
zone, _ := self.region.GetZone()
|
||||
return zone
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SWire) GetStatus() string {
|
||||
return api.WIRE_STATUS_AVAILABLE
|
||||
}
|
||||
|
||||
func (self *SRegion) GetWires() ([]SWire, error) {
|
||||
func (self *SProxmoxClient) GetWires(node string) ([]SWire, error) {
|
||||
ret := []SWire{}
|
||||
wire := &SWire{region: self}
|
||||
ret = append(ret, *wire)
|
||||
|
||||
urlNetwork := fmt.Sprintf("/nodes/%s/network", node)
|
||||
self.get(urlNetwork, url.Values{}, &ret)
|
||||
|
||||
for i := range ret {
|
||||
ret[i].Id = fmt.Sprintf("network/%s/%s", node, ret[i].Iface)
|
||||
ret[i].Node = node
|
||||
ret[i].client = self
|
||||
ret[i].Index = int8(i)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetWire() (*SWire, error) {
|
||||
ret := &SWire{region: self}
|
||||
func (self *SProxmoxClient) GetWire(id string) (*SWire, error) {
|
||||
ret := &SWire{}
|
||||
|
||||
//"id": "network/nodeNAME/iface",
|
||||
splited := strings.Split(id, "/")
|
||||
nodeName := ""
|
||||
networkName := ""
|
||||
|
||||
if len(splited) == 3 {
|
||||
nodeName = splited[1]
|
||||
networkName = splited[2]
|
||||
} else {
|
||||
return nil, errors.Errorf("failed to get network by %s", id)
|
||||
}
|
||||
|
||||
res := fmt.Sprintf("/nodes/%s/network/%s", nodeName, networkName)
|
||||
err := self.get(res, url.Values{}, ret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret.Id = fmt.Sprintf("network/%s/%s", nodeName, networkName)
|
||||
ret.Node = nodeName
|
||||
ret.client = self
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
+7
-113
@@ -18,25 +18,9 @@ import (
|
||||
"net/url"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type SZone struct {
|
||||
multicloud.SResourceBase
|
||||
ProxmoxTags
|
||||
|
||||
region *SRegion
|
||||
|
||||
Name string
|
||||
Nodes int
|
||||
Quorate int
|
||||
Version int
|
||||
}
|
||||
|
||||
type ClusterStatus struct {
|
||||
type SCluster struct {
|
||||
Nodes int `json:"nodes,omitempty"`
|
||||
Quorate int `json:"quorate,omitempty"`
|
||||
Version int `json:"version,omitempty"`
|
||||
@@ -50,108 +34,18 @@ type ClusterStatus struct {
|
||||
IP string `json:"ip,omitempty"`
|
||||
}
|
||||
|
||||
func (self *SZone) GetId() string {
|
||||
return self.Name
|
||||
}
|
||||
|
||||
func (self *SZone) GetGlobalId() string {
|
||||
return self.Name
|
||||
}
|
||||
|
||||
func (self *SZone) GetName() string {
|
||||
return self.Name
|
||||
}
|
||||
|
||||
func (self *SZone) GetIRegion() cloudprovider.ICloudRegion {
|
||||
return self.region
|
||||
}
|
||||
|
||||
func (self *SZone) GetStatus() string {
|
||||
return api.ZONE_ENABLE
|
||||
}
|
||||
|
||||
func (self *SZone) GetI18n() cloudprovider.SModelI18nTable {
|
||||
table := cloudprovider.SModelI18nTable{}
|
||||
table["name"] = cloudprovider.NewSModelI18nEntry(self.GetName()).CN(self.GetName())
|
||||
return table
|
||||
}
|
||||
|
||||
func (self *SZone) GetIHostById(id string) (cloudprovider.ICloudHost, error) {
|
||||
host, err := self.region.GetHost(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
host.zone = self
|
||||
|
||||
return host, nil
|
||||
}
|
||||
|
||||
func (self *SZone) GetIHosts() ([]cloudprovider.ICloudHost, error) {
|
||||
hosts, err := self.region.GetHosts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := []cloudprovider.ICloudHost{}
|
||||
for i := range hosts {
|
||||
hosts[i].zone = self
|
||||
ret = append(ret, &hosts[i])
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SZone) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
|
||||
storages, err := self.region.GetStorages()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := []cloudprovider.ICloudStorage{}
|
||||
for i := range storages {
|
||||
storages[i].zone = self
|
||||
ret = append(ret, &storages[i])
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SZone) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
|
||||
storage, err := self.region.GetStorage(id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetStorage %s", id)
|
||||
}
|
||||
|
||||
storage.zone = self
|
||||
return storage, nil
|
||||
}
|
||||
|
||||
func (self *SRegion) GetZone() (*SZone, error) {
|
||||
ret := &SZone{region: self}
|
||||
css := []ClusterStatus{}
|
||||
|
||||
noneClusterMsg := true
|
||||
nodeNum := 0
|
||||
func (self *SProxmoxClient) GetCluster() (*SCluster, error) {
|
||||
css := []SCluster{}
|
||||
|
||||
err := self.get("/cluster/status", url.Values{}, &css)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, cs := range css {
|
||||
if cs.Type == "cluster" {
|
||||
ret.Name = cs.Name
|
||||
ret.Nodes = cs.Nodes
|
||||
ret.Quorate = cs.Quorate
|
||||
ret.Version = cs.Version
|
||||
noneClusterMsg = false
|
||||
break
|
||||
} else {
|
||||
nodeNum++
|
||||
for i := range css {
|
||||
if css[i].Type == "cluster" {
|
||||
return &css[i], nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if noneClusterMsg == true {
|
||||
ret.Name = "cluster"
|
||||
ret.Nodes = nodeNum
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
return nil, errors.Wrapf(errors.ErrNotFound, "GetCluster")
|
||||
}
|
||||
|
||||
+41
-8
@@ -17,11 +17,14 @@ package compare
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
)
|
||||
|
||||
type SyncResult struct {
|
||||
mu sync.Mutex `json:"-"`
|
||||
|
||||
AddCnt int `json:"add_cnt,omitzero"`
|
||||
AddErrCnt int `json:"add_err_cnt,omitzero"`
|
||||
UpdateCnt int `json:"update_cnt,omitzero"`
|
||||
@@ -31,41 +34,59 @@ type SyncResult struct {
|
||||
errors []error
|
||||
}
|
||||
|
||||
func (self *SyncResult) Error(msg error) {
|
||||
func (self *SyncResult) appendError(msg error) {
|
||||
if self.errors == nil {
|
||||
self.errors = make([]error, 0)
|
||||
}
|
||||
self.errors = append(self.errors, msg)
|
||||
}
|
||||
|
||||
func (self *SyncResult) Error(msg error) {
|
||||
self.mu.Lock()
|
||||
defer self.mu.Unlock()
|
||||
self.appendError(msg)
|
||||
}
|
||||
|
||||
func (self *SyncResult) Add() {
|
||||
self.mu.Lock()
|
||||
defer self.mu.Unlock()
|
||||
self.AddCnt += 1
|
||||
}
|
||||
|
||||
func (self *SyncResult) AddError(msg error) {
|
||||
self.mu.Lock()
|
||||
defer self.mu.Unlock()
|
||||
self.AddErrCnt += 1
|
||||
self.Error(msg)
|
||||
self.appendError(msg)
|
||||
}
|
||||
|
||||
func (self *SyncResult) Update() {
|
||||
self.mu.Lock()
|
||||
defer self.mu.Unlock()
|
||||
self.UpdateCnt += 1
|
||||
}
|
||||
|
||||
func (self *SyncResult) UpdateError(msg error) {
|
||||
self.mu.Lock()
|
||||
defer self.mu.Unlock()
|
||||
self.UpdateErrCnt += 1
|
||||
self.Error(msg)
|
||||
self.appendError(msg)
|
||||
}
|
||||
|
||||
func (self *SyncResult) Delete() {
|
||||
self.mu.Lock()
|
||||
defer self.mu.Unlock()
|
||||
self.DelCnt += 1
|
||||
}
|
||||
|
||||
func (self *SyncResult) DeleteError(msg error) {
|
||||
self.mu.Lock()
|
||||
defer self.mu.Unlock()
|
||||
self.DelErrCnt += 1
|
||||
self.Error(msg)
|
||||
self.appendError(msg)
|
||||
}
|
||||
|
||||
func (self *SyncResult) AllError() error {
|
||||
func (self *SyncResult) allErrorUnlocked() error {
|
||||
msgs := make(map[string]bool)
|
||||
for _, e := range self.errors {
|
||||
msg := e.Error()
|
||||
@@ -80,11 +101,21 @@ func (self *SyncResult) AllError() error {
|
||||
return fmt.Errorf(strings.Join(ret, ";"))
|
||||
}
|
||||
|
||||
func (self *SyncResult) AllError() error {
|
||||
self.mu.Lock()
|
||||
defer self.mu.Unlock()
|
||||
return self.allErrorUnlocked()
|
||||
}
|
||||
|
||||
func (self *SyncResult) IsError() bool {
|
||||
return self.errors != nil && len(self.errors) > 0
|
||||
self.mu.Lock()
|
||||
defer self.mu.Unlock()
|
||||
return len(self.errors) > 0
|
||||
}
|
||||
|
||||
func (self *SyncResult) IsGenerateError() bool {
|
||||
self.mu.Lock()
|
||||
defer self.mu.Unlock()
|
||||
for _, err := range self.errors {
|
||||
if e := errors.Cause(err); e != errors.ErrNotImplemented && e != errors.ErrNotSupported && e != errors.ErrNotEmpty {
|
||||
return true
|
||||
@@ -94,9 +125,11 @@ func (self *SyncResult) IsGenerateError() bool {
|
||||
}
|
||||
|
||||
func (self *SyncResult) Result() string {
|
||||
self.mu.Lock()
|
||||
defer self.mu.Unlock()
|
||||
msg := fmt.Sprintf("removed %d failed %d updated %d failed %d added %d failed %d", self.DelCnt, self.DelErrCnt, self.UpdateCnt, self.UpdateErrCnt, self.AddCnt, self.AddErrCnt)
|
||||
if self.IsError() {
|
||||
msg = fmt.Sprintf("%s;%s", msg, self.AllError())
|
||||
if len(self.errors) > 0 {
|
||||
msg = fmt.Sprintf("%s;%s", msg, self.allErrorUnlocked())
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
+17
-10
@@ -156,7 +156,7 @@ func normalizeOsDistribution(osDist string, imageName string) string {
|
||||
return OS_DIST_KYLIN
|
||||
} else if strings.Contains(osDist, "uos") {
|
||||
return OS_DIST_UOS
|
||||
} else if strings.Contains(osDist, "windows") || regexp.MustCompile(".+win(xp|7|8|10|11|2003|2008|2012|2016|2019|2022)*").MatchString(osDist) {
|
||||
} else if strings.Contains(osDist, "windows") || regexp.MustCompile("(^|[^a-z0-9])win(xp|7|8|10|11|2003|2008|2012|2016|2019|2022)([^a-z0-9]|$)").MatchString(osDist) {
|
||||
for _, ver := range []string{"2003", "2008", "2012", "2016", "2019", "2022"} {
|
||||
if strings.Contains(osDist, ver) {
|
||||
return OS_DIST_WINDOWS_SERVER
|
||||
@@ -197,18 +197,18 @@ var imageVersions = map[string][]string{
|
||||
OS_DIST_OPEN_EULER: {"2.0 SP1", "2.0 SP2", "2.0 SP3", "2.0 SP8", "3.0", "22.03", "23.09"},
|
||||
OS_DIST_EULER_OS: {"2"},
|
||||
// 阿里云Linux:补充1代和2/3代版本
|
||||
OS_DIST_ALIYUN: {"1", "2.1903", "3.2104", "3.2304"},
|
||||
OS_DIST_ALIYUN: {"1", "2.1903", "2", "3.2104", "3.2304", "3"},
|
||||
|
||||
// 阿里云轻量版:补充完整版本
|
||||
OS_DIST_ALIBABA_CLOUD_LINUX: {"2.1903", "3.2104", "3.2304", "3.2404"},
|
||||
OS_DIST_ALIBABA_CLOUD_LINUX: {"2.1903", "2", "3.2104", "3.2304", "3.2404", "3"},
|
||||
// 龙蜥OS:补充7/8系列完整小版本
|
||||
OS_DIST_ANOLIS: {"7.6", "7.9", "8.2", "8.4", "8.6", "8.8", "9.0", "9.2"},
|
||||
OS_DIST_ANOLIS: {"7.6", "7.9", "7", "8.2", "8.4", "8.6", "8.8", "8", "9.0", "9.2", "9"},
|
||||
// Rocky Linux:补充8/9全系列小版本
|
||||
OS_DIST_ROCKY_LINUX: {"8.5", "8.6", "8.7", "8.8", "8.9", "8.10", "9.0", "9.1", "9.2", "9.3", "9.4", "9.5"},
|
||||
OS_DIST_ROCKY_LINUX: {"8.5", "8.6", "8.7", "8.8", "8.9", "8.10", "8", "9.0", "9.1", "9.2", "9.3", "9.4", "9.5", "9"},
|
||||
// Fedora:补充近年主流版本(33到40)
|
||||
OS_DIST_FEDORA: {"33", "34", "35", "36", "37", "38", "39", "40"},
|
||||
// AlmaLinux:补充8/9全系列
|
||||
OS_DIST_ALMA_LINUX: {"8.5", "8.6", "8.7", "8.8", "8.9", "8.10", "9.0", "9.1", "9.2", "9.3", "9.4", "9.5"},
|
||||
OS_DIST_ALMA_LINUX: {"8.5", "8.6", "8.7", "8.8", "8.9", "8.10", "8", "9.0", "9.1", "9.2", "9.3", "9.4", "9.5", "9"},
|
||||
// Amazon Linux:补充1/2/2023版本
|
||||
OS_DIST_AMAZON_LINUX: {"2022", "2023", "1", "2"},
|
||||
|
||||
@@ -242,7 +242,7 @@ func normalizeOsVersion(imageName string, osDist string, osVersion string) strin
|
||||
parts = append(parts, fmt.Sprintf(`(?P<verstr>%s[.\d]*)`, version), "")
|
||||
regexpStr := strings.Join(parts, `[\s-_]*`)
|
||||
m := regexp.MustCompile(regexpStr).FindAllStringSubmatch(strings.ToLower(imageName), -1)
|
||||
if m != nil && len(m) > 0 && len(m[0]) > 1 {
|
||||
if len(m) > 0 && len(m[0]) > 1 {
|
||||
verStr := m[0][1]
|
||||
if strings.HasPrefix(verStr, version) && len(verStr) > len(version) && !strings.HasPrefix(verStr, version+".") {
|
||||
verStr = version + "." + verStr[len(version):]
|
||||
@@ -251,11 +251,18 @@ func normalizeOsVersion(imageName string, osDist string, osVersion string) strin
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := len(versions) - 1; i > 0; i-- {
|
||||
if strings.Contains(imageName, versions[i]) {
|
||||
return versions[i]
|
||||
imageNameLower := strings.ToLower(imageName)
|
||||
bestMatch := ""
|
||||
for i := 0; i < len(versions); i++ {
|
||||
version := strings.ToLower(versions[i])
|
||||
pattern := fmt.Sprintf(`(^|[^0-9])%s([^0-9]|$)`, regexp.QuoteMeta(version))
|
||||
if regexp.MustCompile(pattern).MatchString(imageNameLower) && len(versions[i]) > len(bestMatch) {
|
||||
bestMatch = versions[i]
|
||||
}
|
||||
}
|
||||
if len(bestMatch) > 0 {
|
||||
return bestMatch
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user