mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-19 10:46:58 +08:00
Merge pull request #1094 in YUNIONIO/onecloud from ~TANGBIN/onecloud:bugfix/tb-huawei-region-bugfix to release/2.6.0
* commit '6f43c49bdc08a67e5fcc2daf740224988a445d9b': 不允许删除带快照的华为云硬盘 华为云删除硬盘时删除快照 华为云快照回滚bugfix
This commit is contained in:
@@ -3,6 +3,7 @@ package models
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
@@ -395,6 +396,11 @@ func (manager *SCloudregionManager) ListItemFilter(ctx context.Context, q *sqlch
|
||||
}
|
||||
manager := managerObj.(*SCloudprovider)
|
||||
q = q.Equals("provider", manager.Provider)
|
||||
if manager.Provider == CLOUD_PROVIDER_HUAWEI {
|
||||
region := strings.Split(manager.Name, "_")[0]
|
||||
prefix := CLOUD_PROVIDER_HUAWEI + "/" + region
|
||||
q = q.Startswith("external_id", prefix)
|
||||
}
|
||||
}
|
||||
accountStr, _ := query.GetString("account")
|
||||
if len(accountStr) > 0 {
|
||||
|
||||
@@ -1381,10 +1381,19 @@ func (self *SDisk) PerformPurge(ctx context.Context, userCred mcclient.TokenCred
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if self.GetCloudprovider().Provider == CLOUD_PROVIDER_HUAWEI && self.GetSnapshotCount() > 0 {
|
||||
return nil, httperrors.NewForbiddenError("not allow to purge. Virtual disk must not have snapshots")
|
||||
}
|
||||
|
||||
return nil, self.StartDiskDeleteTask(ctx, userCred, "", true, false)
|
||||
}
|
||||
|
||||
func (self *SDisk) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
|
||||
if self.GetCloudprovider().Provider == CLOUD_PROVIDER_HUAWEI && self.GetSnapshotCount() > 0 {
|
||||
return httperrors.NewForbiddenError("not allow to delete. Virtual disk must not have snapshots")
|
||||
}
|
||||
|
||||
return self.StartDiskDeleteTask(ctx, userCred, "", false,
|
||||
jsonutils.QueryBoolean(query, "override_pending_delete", false))
|
||||
}
|
||||
|
||||
@@ -18,16 +18,16 @@ type IBaseManager interface {
|
||||
|
||||
type IManager interface {
|
||||
IBaseManager
|
||||
// 获取资源列表 GET <base_url>/cloudservers/?<querys>
|
||||
List(querys map[string]string) (*responses.ListResult, error)
|
||||
// 根据上文获取资源列表 GET <base_url>/cloudservers/<cloudserver_id>/nics?<querys>
|
||||
ListInContext(ctx IManagerContext, querys map[string]string) (*responses.ListResult, error)
|
||||
ListInContextWithSpec(ctx IManagerContext, spec string, querys map[string]string, responseKey string) (*responses.ListResult, error)
|
||||
// 获取资源列表 GET <base_url>/cloudservers/?<queries>
|
||||
List(queries map[string]string) (*responses.ListResult, error)
|
||||
// 根据上文获取资源列表 GET <base_url>/cloudservers/<cloudserver_id>/nics?<queries>
|
||||
ListInContext(ctx IManagerContext, queries map[string]string) (*responses.ListResult, error)
|
||||
ListInContextWithSpec(ctx IManagerContext, spec string, queries map[string]string, responseKey string) (*responses.ListResult, error)
|
||||
|
||||
// 查询单个资源 GET <base_url>/cloudservers/<cloudserver_id>?<querys>
|
||||
Get(id string, querys map[string]string) (jsonutils.JSONObject, error)
|
||||
// 根据上文获取资源查询单个资源 GET <base_url>/cloudservers/<cloudserver_id>/nics/<nic_id>?<querys>
|
||||
GetInContext(ctx IManagerContext, id string, querys map[string]string) (jsonutils.JSONObject, error)
|
||||
// 查询单个资源 GET <base_url>/cloudservers/<cloudserver_id>?<queries>
|
||||
Get(id string, queries map[string]string) (jsonutils.JSONObject, error)
|
||||
// 根据上文获取资源查询单个资源 GET <base_url>/cloudservers/<cloudserver_id>/nics/<nic_id>?<queries>
|
||||
GetInContext(ctx IManagerContext, id string, queries map[string]string) (jsonutils.JSONObject, error)
|
||||
|
||||
// 创建单个资源 POST <base_url>/cloudservers
|
||||
Create(params jsonutils.JSONObject) (jsonutils.JSONObject, error)
|
||||
@@ -48,7 +48,7 @@ type IManager interface {
|
||||
// 根据上文删除单个资源 DELETE <base_url>/cloudservers/<cloudserver_id>/nics/<nic_id>
|
||||
DeleteInContext(ctx IManagerContext, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error)
|
||||
// 根据上文和spec删除单个资源
|
||||
DeleteInContextWithSpec(ctx IManagerContext, id string, spec string, params jsonutils.JSONObject, responseKey string) (jsonutils.JSONObject, error)
|
||||
DeleteInContextWithSpec(ctx IManagerContext, id string, spec string, queries map[string]string, params jsonutils.JSONObject, responseKey string) (jsonutils.JSONObject, error)
|
||||
// 批量执行操作 POST <base_url>/cloudservers/<action>
|
||||
// BatchPerformAction(action string, params jsonutils.JSONObject) (jsonutils.JSONObject, error)
|
||||
// 执行操作 POST <base_url>/cloudservers/<cloudserver_id>/<action>
|
||||
|
||||
@@ -118,26 +118,26 @@ func (self *SResourceManager) ListInContext(ctx manager.IManagerContext, queries
|
||||
return self.ListInContextWithSpec(ctx, "", queries, self.KeywordPlural)
|
||||
}
|
||||
|
||||
func (self *SResourceManager) ListInContextWithSpec(ctx manager.IManagerContext, spec string, querys map[string]string, responseKey string) (*responses.ListResult, error) {
|
||||
func (self *SResourceManager) ListInContextWithSpec(ctx manager.IManagerContext, spec string, queries map[string]string, responseKey string) (*responses.ListResult, error) {
|
||||
request := self.newRequest("GET", "", spec, ctx)
|
||||
for k, v := range querys {
|
||||
for k, v := range queries {
|
||||
request.AddQueryParam(k, v)
|
||||
}
|
||||
|
||||
return self._list(request, responseKey)
|
||||
}
|
||||
|
||||
func (self *SResourceManager) Get(id string, querys map[string]string) (jsonutils.JSONObject, error) {
|
||||
return self.GetInContext(nil, id, querys)
|
||||
func (self *SResourceManager) Get(id string, queries map[string]string) (jsonutils.JSONObject, error) {
|
||||
return self.GetInContext(nil, id, queries)
|
||||
}
|
||||
|
||||
func (self *SResourceManager) GetInContext(ctx manager.IManagerContext, id string, querys map[string]string) (jsonutils.JSONObject, error) {
|
||||
return self.GetInContextWithSpec(ctx, id, "", querys, self.Keyword)
|
||||
func (self *SResourceManager) GetInContext(ctx manager.IManagerContext, id string, queries map[string]string) (jsonutils.JSONObject, error) {
|
||||
return self.GetInContextWithSpec(ctx, id, "", queries, self.Keyword)
|
||||
}
|
||||
|
||||
func (self *SResourceManager) GetInContextWithSpec(ctx manager.IManagerContext, id string, spec string, querys map[string]string, responseKey string) (jsonutils.JSONObject, error) {
|
||||
func (self *SResourceManager) GetInContextWithSpec(ctx manager.IManagerContext, id string, spec string, queries map[string]string, responseKey string) (jsonutils.JSONObject, error) {
|
||||
request := self.newRequest("GET", id, spec, ctx)
|
||||
for k, v := range querys {
|
||||
for k, v := range queries {
|
||||
request.AddQueryParam(k, v)
|
||||
}
|
||||
|
||||
@@ -186,11 +186,15 @@ func (self *SResourceManager) Delete(id string, params jsonutils.JSONObject) (js
|
||||
}
|
||||
|
||||
func (self *SResourceManager) DeleteInContext(ctx manager.IManagerContext, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
return self.DeleteInContextWithSpec(ctx, id, "", params, self.Keyword)
|
||||
return self.DeleteInContextWithSpec(ctx, id, "", nil, params, self.Keyword)
|
||||
}
|
||||
|
||||
func (self *SResourceManager) DeleteInContextWithSpec(ctx manager.IManagerContext, id string, spec string, params jsonutils.JSONObject, responseKey string) (jsonutils.JSONObject, error) {
|
||||
func (self *SResourceManager) DeleteInContextWithSpec(ctx manager.IManagerContext, id string, spec string, queries map[string]string, params jsonutils.JSONObject, responseKey string) (jsonutils.JSONObject, error) {
|
||||
request := self.newRequest("DELETE", id, spec, ctx)
|
||||
for k, v := range queries {
|
||||
request.AddQueryParam(k, v)
|
||||
}
|
||||
|
||||
content := getContent(params)
|
||||
if len(content) > 0 {
|
||||
request.SetContent([]byte(content))
|
||||
|
||||
+29
-6
@@ -95,7 +95,6 @@ type SDisk struct {
|
||||
ConsistencygroupID string `json:"consistencygroup_id"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
|
||||
/*下面这些字段也许不需要*/
|
||||
ExpiredTime time.Time
|
||||
}
|
||||
|
||||
@@ -183,7 +182,17 @@ func (self *SDisk) GetBillingType() string {
|
||||
}
|
||||
|
||||
func (self *SDisk) GetExpiredAt() time.Time {
|
||||
return self.ExpiredTime
|
||||
var expiredTime time.Time
|
||||
if self.Metadata.Billing == "1" {
|
||||
res, err := self.storage.zone.region.GetOrderResourceDetail(self.GetId())
|
||||
if err != nil {
|
||||
log.Debugf(err.Error())
|
||||
}
|
||||
|
||||
expiredTime = res.ExpireTime
|
||||
}
|
||||
|
||||
return expiredTime
|
||||
}
|
||||
|
||||
func (self *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) {
|
||||
@@ -340,7 +349,19 @@ func (self *SDisk) Detach() error {
|
||||
return err
|
||||
}
|
||||
|
||||
return cloudprovider.WaitStatus(self, models.DISK_READY, 5*time.Second, 60*time.Second)
|
||||
return cloudprovider.WaitCreated(5*time.Second, 60*time.Second, func() bool {
|
||||
err := self.Refresh()
|
||||
if err != nil {
|
||||
log.Debugf(err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
if self.Status == "available" {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
func (self *SDisk) Attach(device string) error {
|
||||
@@ -357,7 +378,7 @@ func (self *SDisk) Attach(device string) error {
|
||||
// 对于挂载在系统盘盘位(也就是“/dev/sda”或“/dev/vda”挂载点)上的磁盘,当前仅支持离线卸载
|
||||
func (self *SDisk) Reset(ctx context.Context, snapshotId string) (string, error) {
|
||||
mountpoint := self.GetMountpoint()
|
||||
if mountpoint == "/dev/sda" || mountpoint == "/dev/vda" {
|
||||
if len(mountpoint) > 0 {
|
||||
err := self.Detach()
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -374,7 +395,7 @@ func (self *SDisk) Reset(ctx context.Context, snapshotId string) (string, error)
|
||||
return "", err
|
||||
}
|
||||
|
||||
if mountpoint == "/dev/sda" || mountpoint == "/dev/vda" {
|
||||
if len(mountpoint) > 0 {
|
||||
err := self.Attach(mountpoint)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -427,8 +448,10 @@ func (self *SRegion) CreateDisk(zoneId string, category string, name string, siz
|
||||
return disk.ID, err
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-evs/zh-cn_topic_0058762428.html
|
||||
// 默认删除云硬盘关联的所有快照
|
||||
func (self *SRegion) DeleteDisk(diskId string) error {
|
||||
return DoDelete(self.ecsClient.Disks.Delete, diskId, nil, nil)
|
||||
return DoDeleteWithSpec(self.ecsClient.Disks.DeleteInContextWithSpec, nil, diskId, "", nil, nil)
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/util/billing"
|
||||
"yunion.io/x/onecloud/pkg/util/huawei/client/modules"
|
||||
"yunion.io/x/pkg/util/osprofile"
|
||||
"yunion.io/x/pkg/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -555,9 +556,28 @@ func (self *SInstance) GetVNCInfo() (jsonutils.JSONObject, error) {
|
||||
return self.host.zone.region.GetInstanceVNCUrl(self.GetId())
|
||||
}
|
||||
|
||||
func (self *SInstance) NextDeviceName() (string, error) {
|
||||
currents := []string{}
|
||||
for _, item := range self.OSExtendedVolumesVolumesAttached {
|
||||
currents = append(currents, strings.ToLower(item.Device))
|
||||
}
|
||||
|
||||
for i := 0; i < 25; i++ {
|
||||
device := fmt.Sprintf("/dev/sd%s", string(98+i))
|
||||
if ok, _ := utils.InStringArray(device, currents); !ok {
|
||||
return device, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("disk devicename out of index, current deivces: %s", currents)
|
||||
}
|
||||
|
||||
func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error {
|
||||
// todo: calc device
|
||||
return self.host.zone.region.AttachDisk(self.GetId(), diskId, "")
|
||||
device, err := self.NextDeviceName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return self.host.zone.region.AttachDisk(self.GetId(), diskId, device)
|
||||
}
|
||||
|
||||
func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error {
|
||||
@@ -1073,12 +1093,15 @@ func (self *SRegion) GetInstanceVNCUrl(instanceId string) (jsonutils.JSONObject,
|
||||
}
|
||||
|
||||
// https://support.huaweicloud.com/api-ecs/zh-cn_topic_0022472987.html
|
||||
// todo: 指定device
|
||||
// XEN平台虚拟机device为必选参数。
|
||||
func (self *SRegion) AttachDisk(instanceId string, diskId string, device string) error {
|
||||
params := jsonutils.NewDict()
|
||||
volumeObj := jsonutils.NewDict()
|
||||
volumeObj.Add(jsonutils.NewString(diskId), "volumeId")
|
||||
if len(device) > 0 {
|
||||
volumeObj.Add(jsonutils.NewString(device), "device")
|
||||
}
|
||||
|
||||
params.Add(volumeObj, "volumeAttachment")
|
||||
|
||||
_, err := self.ecsClient.Servers.PerformAction2("attachvolume", instanceId, params, "")
|
||||
@@ -1089,7 +1112,7 @@ func (self *SRegion) AttachDisk(instanceId string, diskId string, device string)
|
||||
// 默认非强制卸载。delete_flag=0
|
||||
func (self *SRegion) DetachDisk(instanceId string, diskId string) error {
|
||||
path := fmt.Sprintf("detachvolume/%s", diskId)
|
||||
return DoDeleteWithSpec(self.ecsClient.Servers.DeleteInContextWithSpec, nil, instanceId, path, nil)
|
||||
return DoDeleteWithSpec(self.ecsClient.Servers.DeleteInContextWithSpec, nil, instanceId, path, nil, nil)
|
||||
}
|
||||
|
||||
// 目前无接口支持
|
||||
|
||||
@@ -147,5 +147,5 @@ func (self *SRegion) GetNetwroks(vpcId string) ([]SNetwork, error) {
|
||||
|
||||
func (self *SRegion) deleteNetwork(vpcId string, networkId string) error {
|
||||
ctx := &modules.SManagerContext{InstanceId: vpcId, InstanceManager: self.ecsClient.Vpcs}
|
||||
return DoDeleteWithSpec(self.ecsClient.Subnets.DeleteInContextWithSpec, ctx, networkId, "", nil)
|
||||
return DoDeleteWithSpec(self.ecsClient.Subnets.DeleteInContextWithSpec, ctx, networkId, "", nil, nil)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ type createFunc func(params jsonutils.JSONObject) (jsonutils.JSONObject, error)
|
||||
type updateFunc func(id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error)
|
||||
type updateFunc2 func(ctx manager.IManagerContext, id string, spec string, params jsonutils.JSONObject, responseKey string) (jsonutils.JSONObject, error)
|
||||
type deleteFunc func(id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error)
|
||||
type deleteFunc2 func(ctx manager.IManagerContext, id string, spec string, params jsonutils.JSONObject, responseKey string) (jsonutils.JSONObject, error)
|
||||
type deleteFunc2 func(ctx manager.IManagerContext, id string, spec string, queries map[string]string, params jsonutils.JSONObject, responseKey string) (jsonutils.JSONObject, error)
|
||||
type listInCtxFunc func(ctx manager.IManagerContext, querys map[string]string) (*responses.ListResult, error)
|
||||
type listInCtxWithSpecFunc func(ctx manager.IManagerContext, spec string, querys map[string]string, responseKey string) (*responses.ListResult, error)
|
||||
|
||||
@@ -172,11 +172,11 @@ func DoDelete(deleteFunc deleteFunc, id string, params jsonutils.JSONObject, res
|
||||
return unmarshalResult(ret, err, result)
|
||||
}
|
||||
|
||||
func DoDeleteWithSpec(deleteFunc deleteFunc2, ctx manager.IManagerContext, id string, spec string, params jsonutils.JSONObject) error {
|
||||
func DoDeleteWithSpec(deleteFunc deleteFunc2, ctx manager.IManagerContext, id string, spec string, queries map[string]string, params jsonutils.JSONObject) error {
|
||||
if len(id) == 0 {
|
||||
return fmt.Errorf(" id should not be empty")
|
||||
}
|
||||
|
||||
_, err := deleteFunc(ctx, id, spec, params, "")
|
||||
_, err := deleteFunc(ctx, id, spec, queries, params, "")
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user