diff --git a/pkg/multicloud/incloudsphere/host.go b/pkg/multicloud/incloudsphere/host.go index 3d8c65d96f..1d225985d1 100644 --- a/pkg/multicloud/incloudsphere/host.go +++ b/pkg/multicloud/incloudsphere/host.go @@ -202,6 +202,32 @@ func (self *SHost) GetVersion() string { } func (self *SHost) CreateVM(opts *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) { + storages, err := self.zone.region.GetImageStorages(self.zone.Id) + if err != nil { + return nil, errors.Wrapf(err, "GetImageStorages") + } + var image *SImage = nil + storageCache := &SStoragecache{zone: self.zone} + for i := range storages { + images, err := self.zone.region.GetImageList(storages[i].Id) + if err != nil { + return nil, err + } + for j := range images { + images[j].cache = storageCache + if images[j].GetGlobalId() == opts.ExternalImageId { + image = &images[j] + break + } + } + } + if image == nil { + return nil, errors.Wrapf(cloudprovider.ErrNotFound, opts.ExternalImageId) + } + format := "RAW" + if strings.HasSuffix(opts.ExternalImageId, "qcow2") { + format = "QCOW2" + } disks := []Disks{} disks = append(disks, Disks{ BusModel: "IDE", @@ -210,9 +236,17 @@ func (self *SHost) CreateVM(opts *cloudprovider.SManagedVMCreateConfig) (cloudpr Volume: Volume{ Bootable: true, DataStoreId: opts.SysDisk.StorageExternalId, - Format: "RAW", - Size: float64(opts.SysDisk.SizeGB), + Format: format, + Size: float64(image.GetSizeByte() / 1024 / 1024 / 1024), VolumePolicy: "THIN", + VvSourceDto: VvSourceDto{ + FilePath: fmt.Sprintf("%s/%s", image.Path, image.Name), + SourceType: "IMAGE_STORAGE", + FtpServerHost: FtpServerHost{ + Id: image.ServerID, + Ip: image.FTPServer, + }, + }, }, }) for i := range opts.DataDisks { @@ -229,9 +263,20 @@ func (self *SHost) CreateVM(opts *cloudprovider.SManagedVMCreateConfig) (cloudpr }, }) } - imageInfo := strings.Split(opts.ExternalImageId, "/") - if len(imageInfo) != 3 { - return nil, fmt.Errorf("invalid image id %s", opts.ExternalImageId) + imageTree, err := self.zone.region.GetImageTrees() + if err != nil { + return nil, errors.Wrapf(err, "GetImageTrees") + } + guestLabel, guestosType := "CentOS 7.9(2009) 64bit", "CentOS" + for i := range imageTree { + images := imageTree[i].ToList() + for j := range images { + if images[j].IsEquals(image.Name) { + guestLabel = images[j].Model + guestosType = images[j].OsDist + break + } + } } body := map[string]interface{}{ "boot": "HD", @@ -254,8 +299,8 @@ func (self *SHost) CreateVM(opts *cloudprovider.SManagedVMCreateConfig) (cloudpr "enableReplicate": false, "graphicsCardMemory": 16384, "graphicsCardModel": "VGA", - "guestosLabel": imageInfo[2], - "guestosType": imageInfo[1], + "guestosLabel": guestLabel, + "guestosType": guestosType, "hostBinded": false, "hostId": self.Id, "maxCpuNum": 128, diff --git a/pkg/multicloud/incloudsphere/image.go b/pkg/multicloud/incloudsphere/image.go index 07afc98724..809ce3daf6 100644 --- a/pkg/multicloud/incloudsphere/image.go +++ b/pkg/multicloud/incloudsphere/image.go @@ -30,19 +30,20 @@ type SImage struct { multicloud.InCloudSphereTags cache *SStoragecache - OsType string - OsDist string - Model string - SocketLimit int - SupportCpuHotPlug bool - SupportMemHotPlug bool - SupportDiskHotPlug bool - SupportUefiBootMode bool -} - -func (self *SRegion) GetImages() ([]SImage, error) { - ret := []SImage{} - return ret, self.list("/vms/guestos", nil, &ret) + Name string `json:"name"` + FileSize int64 `json:"fileSize"` + RealSize int64 `json:"realSize"` + SourceType string `json:"sourceType"` + Format string `json:"format"` + FileType string `json:"fileType"` + Size int64 `json:"size"` + Date string `json:"date"` + Path string `json:"path"` + FTPServer string `json:"ftpServer"` + DataStoreID string `json:"dataStoreId"` + DataStoreName string `json:"dataStoreName"` + ServerID string `json:"serverId"` + Md5 string `json:"md5"` } func (self *SImage) GetMinRamSizeMb() int { @@ -50,23 +51,19 @@ func (self *SImage) GetMinRamSizeMb() int { } func (self *SImage) GetId() string { - return self.Model + return fmt.Sprintf("%s/%s/%s", self.cache.zone.Id, self.Path, self.Name) } func (self *SImage) GetName() string { - return self.Model -} - -func (self *SImage) IsEmulated() bool { - return true + return self.Name } func (self *SImage) Delete(ctx context.Context) error { - return cloudprovider.ErrNotSupported + return cloudprovider.ErrNotImplemented } func (self *SImage) GetGlobalId() string { - return fmt.Sprintf("%s/%s/%s", self.OsType, self.OsDist, self.Model) + return self.GetId() } func (self *SImage) GetIStoragecache() cloudprovider.ICloudStoragecache { @@ -86,11 +83,11 @@ func (self *SImage) GetImageType() cloudprovider.TImageType { } func (self *SImage) GetSizeByte() int64 { - return 20 * 1024 * 1024 * 1024 + return self.FileSize } func (self *SImage) GetOsType() cloudprovider.TOsType { - if strings.Contains(strings.ToLower(self.OsType), "windows") { + if strings.Contains(strings.ToLower(self.Name), "windows") { return cloudprovider.OsTypeWindows } return cloudprovider.OsTypeLinux @@ -101,7 +98,7 @@ func (self *SImage) GetOsDist() string { } func (self *SImage) getNormalizedImageInfo() imagetools.ImageInfo { - return imagetools.NormalizeImageInfo(self.Model, "", "", "", "") + return imagetools.NormalizeImageInfo(self.Name, "", "", "", "") } func (self *SImage) GetOsVersion() string { @@ -120,13 +117,30 @@ func (self *SImage) GetMinOsDiskSizeGb() int { } func (self *SImage) GetImageFormat() string { - return "vhd" + return self.Format } func (self *SImage) UEFI() bool { return false } +type SImageInfo struct { + OsType string + OsDist string + Model string + SocketLimit int + SupportCpuHotPlug bool + SupportMemHotPlug bool + SupportDiskHotPlug bool + SupportUefiBootMode bool +} + +func (self *SImageInfo) IsEquals(name string) bool { + model := imagetools.NormalizeImageInfo(self.Model, "", "", "", "") + image := imagetools.NormalizeImageInfo(name, "", "", "", "") + return model.OsDistro == image.OsDistro && model.OsVersion == image.OsVersion +} + type SImageTree struct { Id string Name string @@ -136,13 +150,13 @@ type SImageTree struct { Children []struct { Id string Name string - Object SImage + Object SImageInfo } } } -func (self *SImageTree) ToList() []SImage { - ret := []SImage{} +func (self *SImageTree) ToList() []SImageInfo { + ret := []SImageInfo{} for i := range self.Children { for j := range self.Children[i].Children { self.Children[i].Children[j].Object.OsType = self.Name diff --git a/pkg/multicloud/incloudsphere/instance.go b/pkg/multicloud/incloudsphere/instance.go index 173a7a6155..35986ee462 100644 --- a/pkg/multicloud/incloudsphere/instance.go +++ b/pkg/multicloud/incloudsphere/instance.go @@ -46,45 +46,57 @@ type Floppy struct { VfdType string `json:"vfdType"` } +type FtpServerHost struct { + Id string `json:"id"` + Ip string `json:"ip"` +} + +type VvSourceDto struct { + SourceType string `json:"sourceType"` + FilePath string `json:"filePath"` + Name string `json:"name"` + FtpServerHost FtpServerHost `json:"ftpServerHost"` +} + type Volume struct { - Id string `json:"id"` - UUID string `json:"uuid"` - Size float64 `json:"size"` - RealSize float64 `json:"realSize"` - Name string `json:"name"` - FileName string `json:"fileName"` - Offset int `json:"offset"` - Shared bool `json:"shared"` - DeleteModel string `json:"deleteModel"` - VolumePolicy string `json:"volumePolicy"` - Format string `json:"format"` - BlockDeviceId string `json:"blockDeviceId"` - DiskType string `json:"diskType"` - DataStoreId string `json:"dataStoreId"` - DataStoreName string `json:"dataStoreName"` - DataStoreSize float64 `json:"dataStoreSize"` - FreeStorage float64 `json:"freeStorage"` - DataStoreType string `json:"dataStoreType"` - DataStoreReplicate int `json:"dataStoreReplicate"` - VMName string `json:"vmName"` - VMStatus string `json:"vmStatus"` - Type string `json:"type"` - Description string `json:"description"` - Bootable bool `json:"bootable"` - VolumeStatus string `json:"volumeStatus"` - MountedHostIds string `json:"mountedHostIds"` - Md5 string `json:"md5"` - DataSize int `json:"dataSize"` - OpenStackId string `json:"openStackId"` - VvSourceDto string `json:"vvSourceDto"` - FormatDisk bool `json:"formatDisk"` - ToBeConverted bool `json:"toBeConverted"` - RelatedVms string `json:"relatedVms"` - XactiveDataStoreId string `json:"xactiveDataStoreId"` - ClusterSize int `json:"clusterSize"` - ScsiId string `json:"scsiId"` - SecondaryUUID string `json:"secondaryUuid"` - SecondaryVolumes []struct{} `json:"secondaryVolumes"` + Id string `json:"id"` + UUID string `json:"uuid"` + Size float64 `json:"size"` + RealSize float64 `json:"realSize"` + Name string `json:"name"` + FileName string `json:"fileName"` + Offset int `json:"offset"` + Shared bool `json:"shared"` + DeleteModel string `json:"deleteModel"` + VolumePolicy string `json:"volumePolicy"` + Format string `json:"format"` + BlockDeviceId string `json:"blockDeviceId"` + DiskType string `json:"diskType"` + DataStoreId string `json:"dataStoreId"` + DataStoreName string `json:"dataStoreName"` + DataStoreSize float64 `json:"dataStoreSize"` + FreeStorage float64 `json:"freeStorage"` + DataStoreType string `json:"dataStoreType"` + DataStoreReplicate int `json:"dataStoreReplicate"` + VMName string `json:"vmName"` + VMStatus string `json:"vmStatus"` + Type string `json:"type"` + Description string `json:"description"` + Bootable bool `json:"bootable"` + VolumeStatus string `json:"volumeStatus"` + MountedHostIds string `json:"mountedHostIds"` + Md5 string `json:"md5"` + DataSize int `json:"dataSize"` + OpenStackId string `json:"openStackId"` + VvSourceDto VvSourceDto `json:"vvSourceDto"` + FormatDisk bool `json:"formatDisk"` + ToBeConverted bool `json:"toBeConverted"` + RelatedVms string `json:"relatedVms"` + XactiveDataStoreId string `json:"xactiveDataStoreId"` + ClusterSize int `json:"clusterSize"` + ScsiId string `json:"scsiId"` + SecondaryUUID string `json:"secondaryUuid"` + SecondaryVolumes []struct{} `json:"secondaryVolumes"` } type Disks struct { diff --git a/pkg/multicloud/incloudsphere/region.go b/pkg/multicloud/incloudsphere/region.go index 4074c9ec34..044673dc45 100644 --- a/pkg/multicloud/incloudsphere/region.go +++ b/pkg/multicloud/incloudsphere/region.go @@ -161,19 +161,28 @@ func (self *SRegion) GetIHostById(id string) (cloudprovider.ICloudHost, error) { return host, nil } -func (self *SRegion) getStorageCache() *SStoragecache { - return &SStoragecache{region: self} -} - func (self *SRegion) GetIStoragecaches() ([]cloudprovider.ICloudStoragecache, error) { - cache := self.getStorageCache() - return []cloudprovider.ICloudStoragecache{cache}, nil + zones, err := self.GetZones() + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudStoragecache{} + for i := range zones { + cache := &SStoragecache{zone: &zones[i]} + ret = append(ret, cache) + } + return ret, nil } func (self *SRegion) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { - cache := self.getStorageCache() - if cache.GetGlobalId() == id { - return cache, nil + caches, err := self.GetIStoragecaches() + if err != nil { + return nil, err + } + for i := range caches { + if caches[i].GetGlobalId() == id { + return caches[i], nil + } } return nil, cloudprovider.ErrNotFound } diff --git a/pkg/multicloud/incloudsphere/shell/image.go b/pkg/multicloud/incloudsphere/shell/image.go index c9028e5e55..df86debca9 100644 --- a/pkg/multicloud/incloudsphere/shell/image.go +++ b/pkg/multicloud/incloudsphere/shell/image.go @@ -20,9 +20,9 @@ import ( ) func init() { - type ImageListOptions struct { + type ImageTreeOptions struct { } - shellutils.R(&ImageListOptions{}, "image-list", "list images", func(cli *incloudsphere.SRegion, args *ImageListOptions) error { + shellutils.R(&ImageTreeOptions{}, "image-tree", "list images", func(cli *incloudsphere.SRegion, args *ImageTreeOptions) error { trees, err := cli.GetImageTrees() if err != nil { return err @@ -32,4 +32,31 @@ func init() { } return nil }) + + type ImageStorageListOptions struct { + DS_ID string + } + + shellutils.R(&ImageStorageListOptions{}, "image-storage-list", "list image storages", func(cli *incloudsphere.SRegion, args *ImageStorageListOptions) error { + storages, err := cli.GetImageStorages(args.DS_ID) + if err != nil { + return err + } + printList(storages, 0, 0, 0, []string{}) + return nil + }) + + type ImageListOptions struct { + STORAGE_ID string + } + + shellutils.R(&ImageListOptions{}, "image-list", "list images", func(cli *incloudsphere.SRegion, args *ImageListOptions) error { + images, err := cli.GetImageList(args.STORAGE_ID) + if err != nil { + return err + } + printList(images, 0, 0, 0, []string{}) + return nil + }) + } diff --git a/pkg/multicloud/incloudsphere/sphere.go b/pkg/multicloud/incloudsphere/sphere.go index bb73e0e59d..d4b13053a9 100644 --- a/pkg/multicloud/incloudsphere/sphere.go +++ b/pkg/multicloud/incloudsphere/sphere.go @@ -22,6 +22,7 @@ import ( "strings" "yunion.io/x/jsonutils" + "yunion.io/x/log" "yunion.io/x/pkg/errors" api "yunion.io/x/onecloud/pkg/apis/compute" @@ -121,6 +122,7 @@ func (self SphereError) Error() string { func (ce *SphereError) ParseErrorFromJsonResponse(statusCode int, body jsonutils.JSONObject) error { if body != nil { body.Unmarshal(ce) + log.Errorf("error: %v", body.PrettyString()) } if ce.Code == 0 && statusCode > 0 { ce.Code = statusCode diff --git a/pkg/multicloud/incloudsphere/storage.go b/pkg/multicloud/incloudsphere/storage.go index 4976bdf89e..276081b565 100644 --- a/pkg/multicloud/incloudsphere/storage.go +++ b/pkg/multicloud/incloudsphere/storage.go @@ -152,7 +152,8 @@ func (self *SStorage) GetIDiskById(id string) (cloudprovider.ICloudDisk, error) } func (self *SStorage) GetIStoragecache() cloudprovider.ICloudStoragecache { - return self.zone.region.getStorageCache() + cache := &SStoragecache{zone: self.zone} + return cache } func (self *SStorage) GetMediumType() string { diff --git a/pkg/multicloud/incloudsphere/storagecache.go b/pkg/multicloud/incloudsphere/storagecache.go index 5b5b0788c4..6a9eb0dd31 100644 --- a/pkg/multicloud/incloudsphere/storagecache.go +++ b/pkg/multicloud/incloudsphere/storagecache.go @@ -16,8 +16,11 @@ package incloudsphere import ( "context" + "fmt" + "net/url" "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/mcclient" @@ -27,19 +30,20 @@ import ( type SStoragecache struct { multicloud.SResourceBase multicloud.InCloudSphereTags - region *SRegion + + zone *SZone } func (self *SStoragecache) GetGlobalId() string { - return self.region.GetGlobalId() + return self.zone.GetGlobalId() } func (self *SStoragecache) GetId() string { - return self.region.GetId() + return self.zone.GetId() } func (self *SStoragecache) GetName() string { - return self.region.GetName() + return self.zone.GetName() } func (self *SStoragecache) GetStatus() string { @@ -47,20 +51,22 @@ func (self *SStoragecache) GetStatus() string { } func (self *SStoragecache) GetICloudImages() ([]cloudprovider.ICloudImage, error) { - trees, err := self.region.GetImageTrees() - if err != nil { - return nil, err - } ret := []cloudprovider.ICloudImage{} - gMap := map[string]bool{} - for i := range trees { - images := trees[i].ToList() + iss, err := self.zone.region.GetImageStorages(self.zone.Id) + if err != nil { + return nil, errors.Wrapf(err, "GetImageStorages") + } + for i := range iss { + images, err := self.zone.region.GetImageList(iss[i].Id) + if err != nil { + return nil, err + } for j := range images { - if _, ok := gMap[images[j].GetGlobalId()]; !ok { - images[j].cache = self - ret = append(ret, &images[j]) - gMap[images[j].GetGlobalId()] = true + if images[i].GetImageFormat() == "iso" { + continue } + images[j].cache = self + ret = append(ret, &images[j]) } } return ret, nil @@ -98,3 +104,22 @@ func (self *SStoragecache) DownloadImage(userCred mcclient.TokenCredential, imag func (self *SStoragecache) UploadImage(ctx context.Context, userCred mcclient.TokenCredential, image *cloudprovider.SImageCreateOption, callback func(float32)) (string, error) { return "", cloudprovider.ErrNotSupported } + +type SImageStorage struct { + Id string + Name string +} + +func (self *SRegion) GetImageStorages(dsId string) ([]SImageStorage, error) { + params := url.Values{} + params.Set("type", "imagestorage") + res := fmt.Sprintf("/datacenters/%s/storages", dsId) + ret := []SImageStorage{} + return ret, self.get(res, params, &ret) +} + +func (self *SRegion) GetImageList(storageId string) ([]SImage, error) { + res := fmt.Sprintf("/storages/%s/files", storageId) + ret := []SImage{} + return ret, self.list(res, url.Values{}, &ret) +}