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:
邱剑
2019-02-22 21:24:11 +08:00
8 changed files with 99 additions and 34 deletions
+6
View File
@@ -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 {
+9
View File
@@ -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))
}
+10 -10
View File
@@ -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
View File
@@ -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)
}
/*
+27 -4
View File
@@ -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)
}
// 目前无接口支持
+1 -1
View File
@@ -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)
}
+3 -3
View File
@@ -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
}