From 92c647c0bab16fa2a48e3cb7ad43aca554b050e0 Mon Sep 17 00:00:00 2001 From: TangBin Date: Thu, 21 Feb 2019 11:39:24 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E5=8D=8E=E4=B8=BA=E4=BA=91=E5=BF=AB?= =?UTF-8?q?=E7=85=A7=E5=9B=9E=E6=BB=9Abugfix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/compute/models/cloudregions.go | 6 ++++++ pkg/util/huawei/disk.go | 31 +++++++++++++++++++++++++----- pkg/util/huawei/instance.go | 29 +++++++++++++++++++++++++--- 3 files changed, 58 insertions(+), 8 deletions(-) diff --git a/pkg/compute/models/cloudregions.go b/pkg/compute/models/cloudregions.go index ff668134d6..6c6a2a323b 100644 --- a/pkg/compute/models/cloudregions.go +++ b/pkg/compute/models/cloudregions.go @@ -3,6 +3,7 @@ package models import ( "context" "database/sql" + "strings" "time" "yunion.io/x/jsonutils" @@ -380,6 +381,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 { diff --git a/pkg/util/huawei/disk.go b/pkg/util/huawei/disk.go index 58a131f9e1..6feabb9819 100644 --- a/pkg/util/huawei/disk.go +++ b/pkg/util/huawei/disk.go @@ -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 diff --git a/pkg/util/huawei/instance.go b/pkg/util/huawei/instance.go index b0e30ab46f..37ae0915e5 100644 --- a/pkg/util/huawei/instance.go +++ b/pkg/util/huawei/instance.go @@ -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, "") From 4f8a2fafa0fd87ab54ab40145f2d8d105619bfc7 Mon Sep 17 00:00:00 2001 From: TangBin Date: Thu, 21 Feb 2019 17:40:52 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E5=8D=8E=E4=B8=BA=E4=BA=91=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E7=A1=AC=E7=9B=98=E6=97=B6=E5=88=A0=E9=99=A4=E5=BF=AB?= =?UTF-8?q?=E7=85=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/util/huawei/client/manager/manager.go | 20 ++++++++-------- .../huawei/client/modules/manager_resource.go | 24 +++++++++++-------- pkg/util/huawei/disk.go | 5 +++- pkg/util/huawei/instance.go | 2 +- pkg/util/huawei/network.go | 2 +- pkg/util/huawei/utils.go | 6 ++--- 6 files changed, 33 insertions(+), 26 deletions(-) diff --git a/pkg/util/huawei/client/manager/manager.go b/pkg/util/huawei/client/manager/manager.go index d6b7c364e8..5fa16531a8 100644 --- a/pkg/util/huawei/client/manager/manager.go +++ b/pkg/util/huawei/client/manager/manager.go @@ -18,16 +18,16 @@ type IBaseManager interface { type IManager interface { IBaseManager - // 获取资源列表 GET /cloudservers/? - List(querys map[string]string) (*responses.ListResult, error) - // 根据上文获取资源列表 GET /cloudservers//nics? - 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 /cloudservers/? + List(queries map[string]string) (*responses.ListResult, error) + // 根据上文获取资源列表 GET /cloudservers//nics? + 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 /cloudservers/? - Get(id string, querys map[string]string) (jsonutils.JSONObject, error) - // 根据上文获取资源查询单个资源 GET /cloudservers//nics/? - GetInContext(ctx IManagerContext, id string, querys map[string]string) (jsonutils.JSONObject, error) + // 查询单个资源 GET /cloudservers/? + Get(id string, queries map[string]string) (jsonutils.JSONObject, error) + // 根据上文获取资源查询单个资源 GET /cloudservers//nics/? + GetInContext(ctx IManagerContext, id string, queries map[string]string) (jsonutils.JSONObject, error) // 创建单个资源 POST /cloudservers Create(params jsonutils.JSONObject) (jsonutils.JSONObject, error) @@ -48,7 +48,7 @@ type IManager interface { // 根据上文删除单个资源 DELETE /cloudservers//nics/ 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 /cloudservers/ // BatchPerformAction(action string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) // 执行操作 POST /cloudservers// diff --git a/pkg/util/huawei/client/modules/manager_resource.go b/pkg/util/huawei/client/modules/manager_resource.go index 236fda6ce3..b293c4a0ab 100644 --- a/pkg/util/huawei/client/modules/manager_resource.go +++ b/pkg/util/huawei/client/modules/manager_resource.go @@ -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)) diff --git a/pkg/util/huawei/disk.go b/pkg/util/huawei/disk.go index 6feabb9819..e890942ae0 100644 --- a/pkg/util/huawei/disk.go +++ b/pkg/util/huawei/disk.go @@ -448,8 +448,11 @@ 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) + queries := map[string]string{"cascade": "true"} + return DoDeleteWithSpec(self.ecsClient.Disks.DeleteInContextWithSpec, nil, diskId, "", queries, nil) } /* diff --git a/pkg/util/huawei/instance.go b/pkg/util/huawei/instance.go index 37ae0915e5..685b5ec456 100644 --- a/pkg/util/huawei/instance.go +++ b/pkg/util/huawei/instance.go @@ -1112,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) } // 目前无接口支持 diff --git a/pkg/util/huawei/network.go b/pkg/util/huawei/network.go index ced2556770..7a71b3a8b5 100644 --- a/pkg/util/huawei/network.go +++ b/pkg/util/huawei/network.go @@ -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) } diff --git a/pkg/util/huawei/utils.go b/pkg/util/huawei/utils.go index 79b839befb..a7c0eac780 100644 --- a/pkg/util/huawei/utils.go +++ b/pkg/util/huawei/utils.go @@ -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 } From 6f43c49bdc08a67e5fcc2daf740224988a445d9b Mon Sep 17 00:00:00 2001 From: TangBin Date: Thu, 21 Feb 2019 20:15:43 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E4=B8=8D=E5=85=81=E8=AE=B8=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E5=B8=A6=E5=BF=AB=E7=85=A7=E7=9A=84=E5=8D=8E=E4=B8=BA?= =?UTF-8?q?=E4=BA=91=E7=A1=AC=E7=9B=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/compute/models/disks.go | 9 +++++++++ pkg/util/huawei/disk.go | 3 +-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/pkg/compute/models/disks.go b/pkg/compute/models/disks.go index 6bb8768014..b2430e1ce1 100644 --- a/pkg/compute/models/disks.go +++ b/pkg/compute/models/disks.go @@ -1348,10 +1348,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)) } diff --git a/pkg/util/huawei/disk.go b/pkg/util/huawei/disk.go index e890942ae0..5e19812ea9 100644 --- a/pkg/util/huawei/disk.go +++ b/pkg/util/huawei/disk.go @@ -451,8 +451,7 @@ func (self *SRegion) CreateDisk(zoneId string, category string, name string, siz // https://support.huaweicloud.com/api-evs/zh-cn_topic_0058762428.html // 默认删除云硬盘关联的所有快照 func (self *SRegion) DeleteDisk(diskId string) error { - queries := map[string]string{"cascade": "true"} - return DoDeleteWithSpec(self.ecsClient.Disks.DeleteInContextWithSpec, nil, diskId, "", queries, nil) + return DoDeleteWithSpec(self.ecsClient.Disks.DeleteInContextWithSpec, nil, diskId, "", nil, nil) } /*