Files
cloudpods/pkg/multicloud/zstack/disk.go
2021-05-28 20:18:10 +08:00

420 lines
11 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 zstack
import (
"context"
"fmt"
"net/url"
"strings"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/multicloud"
)
type SDisk struct {
multicloud.SDisk
multicloud.ZStackTags
localStorage *SLocalStorage
storage *SStorage
region *SRegion
ZStackBasic
PrimaryStorageUUID string `json:"primaryStorageUuid"`
VMInstanceUUID string `json:"vmInstanceUuid"`
DiskOfferingUUID string `json:"diskOfferingUuid"`
RootImageUUID string `json:"rootImageUuid"`
InstallPath string `json:"installPath"`
Type string `json:"type"`
Format string `json:"format"`
Size int `json:"size"`
ActualSize int `json:"actualSize"`
DeviceID float32 `json:"deviceId"`
State string `json:"state"`
Status string `json:"status"`
ZStackTime
}
func (region *SRegion) GetDisk(diskId string) (*SDisk, error) {
disk := &SDisk{region: region}
err := region.client.getResource("volumes", diskId, disk)
if err != nil {
return nil, err
}
if disk.Status == "NotInstantiated" || disk.Status == "Deleted" {
return nil, cloudprovider.ErrNotFound
}
return disk, nil
}
func (region *SRegion) GetDiskWithStorage(diskId string) (*SDisk, error) {
disk, err := region.GetDisk(diskId)
if err != nil {
log.Errorf("Get Disk %s error: %v", diskId, err)
return nil, err
}
storage, err := region.GetStorage(disk.PrimaryStorageUUID)
if err != nil {
log.Errorf("Get primary storage %s error: %v", disk.PrimaryStorageUUID, err)
return nil, err
}
switch storage.Type {
case StorageTypeLocal:
if len(disk.VMInstanceUUID) > 0 {
instance, err := region.GetInstance(disk.VMInstanceUUID)
if err != nil {
return nil, err
}
hostId := instance.LastHostUUID
if len(hostId) == 0 {
hostId = instance.HostUUID
}
disk.localStorage = &SLocalStorage{region: region, primaryStorageID: storage.UUID, HostUUID: hostId}
return disk, nil
}
tags, err := region.GetResourceSysTags("", "VolumeVO", disk.UUID, "")
if err != nil {
log.Errorf("get disk tag error: %v", err)
return nil, err
}
for i := 0; i < len(tags); i++ {
if strings.HasPrefix(tags[i].Tag, "localStorage::hostUuid::") {
hostInfo := strings.Split(tags[i].Tag, "localStorage::hostUuid::")
if len(hostInfo) == 2 {
localStorage, err := region.GetLocalStorage(storage.UUID, hostInfo[1])
if err != nil {
return nil, err
}
disk.localStorage = localStorage
return disk, nil
}
return nil, fmt.Errorf("invalid host info %s from disk %s", tags[i].Tag, disk.Name)
}
}
return nil, cloudprovider.ErrNotFound
case StorageTypeCeph:
disk.storage = storage
return disk, nil
default:
return nil, fmt.Errorf("Unsupport StorageType %s", storage.Type)
}
}
func (region *SRegion) GetDisks(storageId string, diskIds []string, diskType string) ([]SDisk, error) {
disks := []SDisk{}
params := url.Values{}
params.Add("q", "status!=Deleted")
params.Add("q", "status!=NotInstantiated")
if len(storageId) > 0 {
params.Add("q", "primaryStorageUuid="+storageId)
}
if len(diskIds) > 0 {
params.Add("q", "uuid?="+strings.Join(diskIds, ","))
}
if len(diskType) > 0 {
params.Add("q", "type="+diskType)
}
return disks, region.client.listAll("volumes", params, &disks)
}
func (disk *SDisk) GetSysTags() map[string]string {
data := map[string]string{}
data["hypervisor"] = api.HYPERVISOR_ZSTACK
return data
}
func (disk *SDisk) GetId() string {
return disk.UUID
}
func (disk *SDisk) Delete(ctx context.Context) error {
if disk.Status == "Deleted" {
return disk.region.ExpungeDisk(disk.UUID)
}
return disk.region.DeleteDisk(disk.UUID)
}
func (disk *SDisk) Resize(ctx context.Context, sizeMb int64) error {
return disk.region.ResizeDisk(disk.UUID, sizeMb)
}
func (disk *SDisk) GetName() string {
return disk.Name
}
func (disk *SDisk) GetGlobalId() string {
return disk.UUID
}
func (disk *SDisk) IsEmulated() bool {
return false
}
func (disk *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) {
if disk.localStorage != nil {
return disk.localStorage, nil
}
if disk.storage != nil {
return disk.storage, nil
}
return nil, cloudprovider.ErrNotFound
}
func (disk *SDisk) GetIStorageId() string {
storage, err := disk.region.GetStorage(disk.PrimaryStorageUUID)
if err != nil {
return disk.PrimaryStorageUUID
} else if storage.Type == StorageTypeLocal && len(disk.VMInstanceUUID) > 0 {
instnace, err := disk.region.GetInstance(disk.VMInstanceUUID)
if err != nil {
log.Warningf("failed to get instance %s for disk %s(%s) error: %v", disk.VMInstanceUUID, disk.Name, disk.UUID, err)
return ""
}
return fmt.Sprintf("%s/%s", disk.PrimaryStorageUUID, instnace.LastHostUUID)
}
return disk.PrimaryStorageUUID
}
func (disk *SDisk) GetStatus() string {
switch disk.Status {
case "Ready":
return api.DISK_READY
case "NotInstantiated":
//数据云盘特有的状态。在这个连接状态中数据云盘只存在于数据库的表记录中。NotInstantiated状态的数据云盘可以挂载到任何类型虚拟机管理程序管理的云主机上当挂载到云主机上后数据云盘的hypervisorType域会存储云主机对应的虚拟机管理程序类型在主存储上被实例化为虚拟机管理程序类型的实际二进制文件同时连接状态会改为就绪Ready在这之后这些数据云盘就只能被重新挂载到相同类型虚拟机管理程序管理的云主机上了。
return api.DISK_INIT
case "Creating":
return api.DISK_ALLOCATING
case "Deleted":
return api.DISK_DEALLOC
default:
log.Errorf("Unknown disk %s(%s) status %s", disk.Name, disk.UUID, disk.Status)
return api.DISK_UNKNOWN
}
}
func (disk *SDisk) Refresh() error {
new, err := disk.region.GetDisk(disk.UUID)
if err != nil {
return err
}
return jsonutils.Update(disk, new)
}
func (disk *SDisk) GetDiskFormat() string {
return disk.Format
}
func (disk *SDisk) GetDiskSizeMB() int {
return disk.Size / 1024 / 1024
}
func (disk *SDisk) GetIsAutoDelete() bool {
return disk.GetDiskType() == api.DISK_TYPE_SYS || disk.localStorage != nil
}
func (disk *SDisk) GetTemplateId() string {
return disk.RootImageUUID
}
func (disk *SDisk) GetDiskType() string {
switch disk.Type {
case "Root":
return api.DISK_TYPE_SYS
default:
return api.DISK_TYPE_DATA
}
}
func (disk *SDisk) GetFsFormat() string {
return ""
}
func (disk *SDisk) GetIsNonPersistent() bool {
return false
}
func (disk *SDisk) GetDriver() string {
return "scsi"
}
func (disk *SDisk) GetCacheMode() string {
return "none"
}
func (disk *SDisk) GetMountpoint() string {
return ""
}
func (region *SRegion) CreateDisk(name string, storageId string, hostId string, poolName string, sizeGb int, desc string) (*SDisk, error) {
offerings, err := region.GetDiskOfferings(sizeGb)
if err != nil {
return nil, err
}
diskOfferingUuid := ""
if len(offerings) > 0 {
diskOfferingUuid = offerings[0].UUID
} else {
offering, err := region.CreateDiskOffering(sizeGb)
if err != nil {
return nil, err
}
diskOfferingUuid = offering.UUID
defer region.DeleteDiskOffering(diskOfferingUuid)
}
params := map[string]interface{}{
"params": map[string]string{
"name": name,
"description": desc,
"diskOfferingUuid": diskOfferingUuid,
"primaryStorageUuid": storageId,
},
}
if len(hostId) > 0 {
params["systemTags"] = []string{"localStorage::hostUuid::" + hostId}
}
if len(poolName) > 0 {
params["systemTags"] = []string{"ceph::pool::" + poolName}
}
resp, err := region.client.post("volumes/data", jsonutils.Marshal(params))
if err != nil {
return nil, err
}
disk := &SDisk{region: region}
return disk, resp.Unmarshal(disk, "inventory")
}
func (region *SRegion) ExpungeDisk(diskId string) error {
params := map[string]interface{}{
"expungeDataVolume": jsonutils.NewDict(),
}
_, err := region.client.put("volumes", diskId, jsonutils.Marshal(params))
return err
}
func (region *SRegion) DeleteDisk(diskId string) error {
err := region.client.delete("volumes", diskId, "Enforcing")
if err != nil {
return err
}
return region.ExpungeDisk(diskId)
}
func (region *SRegion) ResizeDisk(diskId string, sizeMb int64) error {
disk, err := region.GetDisk(diskId)
if err != nil {
return err
}
if disk.GetDiskSizeMB() == int(sizeMb) {
return nil
}
params := jsonutils.Marshal(map[string]interface{}{
fmt.Sprintf("resize%sVolume", disk.Type): map[string]int64{
"size": sizeMb * 1024 * 1024,
},
})
resource := "volumes/resize"
if disk.Type == "Data" {
resource = "volumes/data/resize"
}
_, err = region.client.put(resource, diskId, params)
return err
}
func (disk *SDisk) CreateISnapshot(ctx context.Context, name, desc string) (cloudprovider.ICloudSnapshot, error) {
return disk.region.CreateSnapshot(name, disk.UUID, desc)
}
func (disk *SDisk) GetISnapshot(snapshotId string) (cloudprovider.ICloudSnapshot, error) {
snapshots, err := disk.region.GetSnapshots(snapshotId, disk.UUID)
if err != nil {
return nil, err
}
if len(snapshots) == 1 {
if snapshots[0].UUID == snapshotId {
return &snapshots[0], nil
}
return nil, cloudprovider.ErrNotFound
}
if len(snapshots) > 1 {
return nil, cloudprovider.ErrDuplicateId
}
return nil, cloudprovider.ErrNotFound
}
func (disk *SDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) {
snapshots, err := disk.region.GetSnapshots("", disk.UUID)
if err != nil {
return nil, err
}
isnapshots := []cloudprovider.ICloudSnapshot{}
for i := 0; i < len(snapshots); i++ {
isnapshots = append(isnapshots, &snapshots[i])
}
return isnapshots, nil
}
func (disk *SDisk) Reset(ctx context.Context, snapshotId string) (string, error) {
_, err := disk.region.ResetDisks(snapshotId)
return disk.UUID, err
}
func (region *SRegion) ResetDisks(snapshotId string) (jsonutils.JSONObject, error) {
params := map[string]interface{}{
"revertVolumeFromSnapshot": jsonutils.NewDict(),
}
return region.client.put("volume-snapshots", snapshotId, jsonutils.Marshal(params))
}
func (disk *SDisk) GetBillingType() string {
return ""
}
func (disk *SDisk) GetExpiredAt() time.Time {
return time.Time{}
}
func (disk *SDisk) GetCreatedAt() time.Time {
return disk.CreateDate
}
func (disk *SDisk) GetAccessPath() string {
return disk.InstallPath
}
func (disk *SDisk) Rebuild(ctx context.Context) error {
return disk.region.RebuildDisk(disk.UUID)
}
func (region *SRegion) RebuildDisk(diskId string) error {
params := map[string]interface{}{
"recoverDataVolume": jsonutils.NewDict(),
}
_, err := region.client.put("volumes", diskId, jsonutils.Marshal(params))
return err
}
func (disk *SDisk) GetProjectId() string {
return ""
}