From a3c676f7e8f44bbc052f5c167ee12cd16b27b2e1 Mon Sep 17 00:00:00 2001 From: Qu Xuan Date: Fri, 9 Apr 2021 17:21:08 +0800 Subject: [PATCH] fix: nas for huawei --- pkg/compute/models/cloudsync.go | 1 + pkg/compute/models/filesystem.go | 19 +- pkg/compute/regiondrivers/huawei.go | 4 + pkg/compute/tasks/filesystem_create_task.go | 2 +- pkg/multicloud/huawei/client/client.go | 3 + .../huawei/client/modules/manager_resource.go | 2 + .../huawei/client/modules/mod_sfs.go | 48 +++ pkg/multicloud/huawei/huawei.go | 1 + pkg/multicloud/huawei/sfs-turbo.go | 297 ++++++++++++++++++ pkg/multicloud/huawei/shell/filesystem.go | 60 ++++ 10 files changed, 433 insertions(+), 4 deletions(-) create mode 100644 pkg/multicloud/huawei/client/modules/mod_sfs.go create mode 100644 pkg/multicloud/huawei/sfs-turbo.go create mode 100644 pkg/multicloud/huawei/shell/filesystem.go diff --git a/pkg/compute/models/cloudsync.go b/pkg/compute/models/cloudsync.go index b4edff51bf..785c20b114 100644 --- a/pkg/compute/models/cloudsync.go +++ b/pkg/compute/models/cloudsync.go @@ -1200,6 +1200,7 @@ func syncPublicCloudProviderInfo( syncRegionSkus(ctx, userCred, localRegion) SyncRegionDBInstanceSkus(ctx, userCred, localRegion.Id, true) SyncRegionNatSkus(ctx, userCred, localRegion.Id, true) + SyncRegionNasSkus(ctx, userCred, localRegion.Id, true) } else { syncSkusFromPrivateCloud(ctx, userCred, syncResults, localRegion, remoteRegion) } diff --git a/pkg/compute/models/filesystem.go b/pkg/compute/models/filesystem.go index 901b4db9f8..5b561dc7a8 100644 --- a/pkg/compute/models/filesystem.go +++ b/pkg/compute/models/filesystem.go @@ -132,10 +132,23 @@ func (man *SFileSystemManager) ValidateCreateData(ctx context.Context, userCred return input, err } network := net.(*SNetwork) - zone := network.GetZone() input.ManagerId = network.GetVpc().ManagerId - input.ZoneId = zone.Id - input.CloudregionId = zone.CloudregionId + if zone := network.GetZone(); zone != nil { + input.ZoneId = zone.Id + input.CloudregionId = zone.CloudregionId + } else { + zones, err := network.GetRegion().GetZones() + if err != nil { + return input, httperrors.NewGeneralError(errors.Wrapf(err, "GetZones")) + } + for _, zone := range zones { + if zone.Status == api.ZONE_ENABLE { + input.ZoneId = zone.Id + input.CloudregionId = zone.CloudregionId + break + } + } + } } else if len(input.ZoneId) > 0 { _zone, err := validators.ValidateModel(userCred, ZoneManager, &input.ZoneId) if err != nil { diff --git a/pkg/compute/regiondrivers/huawei.go b/pkg/compute/regiondrivers/huawei.go index 25bb6d61a4..e54ae41b5b 100644 --- a/pkg/compute/regiondrivers/huawei.go +++ b/pkg/compute/regiondrivers/huawei.go @@ -2689,3 +2689,7 @@ func (self *SHuaWeiRegionDriver) ValidateCreateNatGateway(ctx context.Context, u func (self *SHuaWeiRegionDriver) IsSupportedNatGateway() bool { return true } + +func (self *SHuaWeiRegionDriver) IsSupportedNas() bool { + return true +} diff --git a/pkg/compute/tasks/filesystem_create_task.go b/pkg/compute/tasks/filesystem_create_task.go index f5ab3412f1..b534309046 100644 --- a/pkg/compute/tasks/filesystem_create_task.go +++ b/pkg/compute/tasks/filesystem_create_task.go @@ -76,7 +76,7 @@ func (self *FileSystemCreateTask) OnInit(ctx context.Context, obj db.IStandalone opts.VpcId = network.GetVpc().ExternalId } - log.Infof("nas create params: %v", opts) + log.Infof("nas create params: %s", jsonutils.Marshal(opts).String()) iFs, err := iRegion.CreateICloudFileSystem(opts) if err != nil { diff --git a/pkg/multicloud/huawei/client/client.go b/pkg/multicloud/huawei/client/client.go index 6ceec2a085..b3286d7fa8 100644 --- a/pkg/multicloud/huawei/client/client.go +++ b/pkg/multicloud/huawei/client/client.go @@ -89,6 +89,7 @@ type Client struct { Groups *modules.SGroupManager SAMLProviders *modules.SAMLProviderManager SAMLProviderMappings *modules.SAMLProviderMappingManager + SfsTurbos *modules.SfsTurboManager } func (self *Client) SetHttpClient(httpClient *http.Client) { @@ -146,6 +147,7 @@ func (self *Client) SetHttpClient(httpClient *http.Client) { self.Groups.SetHttpClient(httpClient) self.SAMLProviders.SetHttpClient(httpClient) self.SAMLProviderMappings.SetHttpClient(httpClient) + self.SfsTurbos.SetHttpClient(httpClient) } func (self *Client) InitWithOptions(regionId, domainId, projectId string, credential auth.Credential) error { @@ -237,6 +239,7 @@ func (self *Client) initManagers() { self.SAMLProviders.SetDomainId(self.domainId) self.SAMLProviderMappings = modules.NewSAMLProviderMappingManager(self.signer, self.debug) self.SAMLProviderMappings.SetDomainId(self.domainId) + self.SfsTurbos = modules.NewSfsTurboManager(self.regionId, self.projectId, self.signer, self.debug) } self.init = true diff --git a/pkg/multicloud/huawei/client/modules/manager_resource.go b/pkg/multicloud/huawei/client/modules/manager_resource.go index b769e0bb44..1b47d1f4f2 100644 --- a/pkg/multicloud/huawei/client/modules/manager_resource.go +++ b/pkg/multicloud/huawei/client/modules/manager_resource.go @@ -51,6 +51,8 @@ const ( ServiceNameCTS ServiceNameType = "cts" // 云审计服务 ServiceNameCES ServiceNameType = "ces" // 监控服务 CloudEye ServiceNameEPS ServiceNameType = "eps" // 企业项目 + + ServiceNameSFSTurbo ServiceNameType = "sfs-turbo" // 文件系统 ) type SManagerContext struct { diff --git a/pkg/multicloud/huawei/client/modules/mod_sfs.go b/pkg/multicloud/huawei/client/modules/mod_sfs.go new file mode 100644 index 0000000000..de29920478 --- /dev/null +++ b/pkg/multicloud/huawei/client/modules/mod_sfs.go @@ -0,0 +1,48 @@ +// 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 modules + +import ( + "yunion.io/x/jsonutils" + + "yunion.io/x/onecloud/pkg/multicloud/huawei/client/auth" + "yunion.io/x/onecloud/pkg/multicloud/huawei/client/responses" +) + +type SfsTurboManager struct { + SResourceManager +} + +func NewSfsTurboManager(regionId, projectId string, signer auth.Signer, debug bool) *SfsTurboManager { + return &SfsTurboManager{SResourceManager: SResourceManager{ + SBaseManager: NewBaseManager(signer, debug), + ServiceName: ServiceNameSFSTurbo, + Region: regionId, + ProjectId: projectId, + version: "v1", + Keyword: "", + KeywordPlural: "shares", + + ResourceKeyword: "sfs-turbo/shares", + }} +} + +func (self *SfsTurboManager) List(querys map[string]string) (*responses.ListResult, error) { + return self.ListInContextWithSpec(nil, "detail", querys, self.KeywordPlural) +} + +func (self *SfsTurboManager) Create(params jsonutils.JSONObject) (jsonutils.JSONObject, error) { + return self.CreateInContextWithSpec(self.ctx, "", params, "") +} diff --git a/pkg/multicloud/huawei/huawei.go b/pkg/multicloud/huawei/huawei.go index 7dfaa498da..23cc17a375 100644 --- a/pkg/multicloud/huawei/huawei.go +++ b/pkg/multicloud/huawei/huawei.go @@ -506,6 +506,7 @@ func (self *SHuaweiClient) GetCapabilities() []string { cloudprovider.CLOUD_CAPABILITY_CLOUDID, cloudprovider.CLOUD_CAPABILITY_SAML_AUTH, cloudprovider.CLOUD_CAPABILITY_NAT, + cloudprovider.CLOUD_CAPABILITY_NAS, } // huawei objectstore is shared across projects(subscriptions) // to avoid multiple project access the same bucket diff --git a/pkg/multicloud/huawei/sfs-turbo.go b/pkg/multicloud/huawei/sfs-turbo.go new file mode 100644 index 0000000000..d874f9db2b --- /dev/null +++ b/pkg/multicloud/huawei/sfs-turbo.go @@ -0,0 +1,297 @@ +// 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 huawei + +import ( + "fmt" + "strings" + "time" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + billing_api "yunion.io/x/onecloud/pkg/apis/billing" + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" +) + +type SfsTurbo struct { + multicloud.SNasBase + region *SRegion + + EnterpriseProjectId string + Actions []string + AvailCapacity float64 + AvailabilityZone string + AzName string + CreatedAt time.Time + CryptKeyId string + ExpandType string + ExportLocation string + Id string + Name string + PayModel string + Region string + SecurityGroupId string + ShareProto string + ShareType string + Size float64 + Status string + SubStatus string + SubnetId string + VpcId string + Description string +} + +func (self *SfsTurbo) GetName() string { + return self.Name +} + +func (self *SfsTurbo) GetId() string { + return self.Id +} + +func (self *SfsTurbo) GetGlobalId() string { + return self.Id +} + +func (self *SfsTurbo) GetFileSystemType() string { + return "SFS Turbo" +} + +func (self *SfsTurbo) Refresh() error { + sf, err := self.region.GetSfsTurbo(self.Id) + if err != nil { + return errors.Wrapf(err, "GetSfsTurbo") + } + return jsonutils.Update(self, sf) +} + +func (self *SfsTurbo) GetBillingType() string { + if self.PayModel == "0" { + return billing_api.BILLING_TYPE_POSTPAID + } + return billing_api.BILLING_TYPE_PREPAID +} + +func (self *SfsTurbo) GetStorageType() string { + if len(self.ExpandType) == 0 { + return strings.ToLower(self.ShareType) + } + return strings.ToLower(self.ShareType) + ".enhanced" +} + +func (self *SfsTurbo) GetProtocol() string { + return self.ShareProto +} + +func (self *SfsTurbo) GetStatus() string { + switch self.Status { + case "100": + return api.NAS_STATUS_CREATING + case "200": + return api.NAS_SKU_AVAILABLE + case "300": + return api.NAS_STATUS_UNKNOWN + case "303": + return api.NAS_STATUS_CREATE_FAILED + case "400": + return api.NAS_STATUS_DELETING + case "800": + return api.NAS_STATUS_UNAVAILABLE + default: + return self.Status + } +} + +func (self *SfsTurbo) GetCreatedAt() time.Time { + return self.CreatedAt +} + +func (self *SfsTurbo) GetCapacityGb() int64 { + return int64(self.Size) +} + +func (self *SfsTurbo) GetUsedCapacityGb() int64 { + return int64(self.Size - self.AvailCapacity) +} + +func (self *SfsTurbo) GetMountTargetCountLimit() int { + return 1 +} + +func (self *SfsTurbo) GetZoneId() string { + return self.AvailabilityZone +} + +func (self *SfsTurbo) GetMountTargets() ([]cloudprovider.ICloudMountTarget, error) { + mt := &sMoutTarget{sfs: self} + return []cloudprovider.ICloudMountTarget{mt}, nil +} + +func (self *SfsTurbo) CreateMountTarget(opts *cloudprovider.SMountTargetCreateOptions) (cloudprovider.ICloudMountTarget, error) { + return nil, errors.Wrap(cloudprovider.ErrNotSupported, "CreateMountTarget") +} + +func (self *SfsTurbo) Delete() error { + return self.region.DeleteSfsTurbo(self.Id) +} + +func (self *SRegion) GetICloudFileSystems() ([]cloudprovider.ICloudFileSystem, error) { + sfs, err := self.GetSfsTurbos() + if err != nil { + return nil, errors.Wrapf(err, "self.GetSfsTurbos") + } + ret := []cloudprovider.ICloudFileSystem{} + for i := range sfs { + sfs[i].region = self + ret = append(ret, &sfs[i]) + } + return ret, nil +} + +func (self *SRegion) GetICloudFileSystemById(id string) (cloudprovider.ICloudFileSystem, error) { + sf, err := self.GetSfsTurbo(id) + if err != nil { + return nil, errors.Wrapf(err, "GetSfsTurbo(%s)", id) + } + return sf, nil +} + +func (self *SRegion) GetSfsTurbos() ([]SfsTurbo, error) { + queues := make(map[string]string) + sfs := make([]SfsTurbo, 0, 2) + err := doListAllWithOffset(self.ecsClient.SfsTurbos.List, queues, &sfs) + if err != nil { + return nil, errors.Wrapf(err, "doListAllWithOffset") + } + return sfs, nil +} + +func (self *SRegion) GetSfsTurbo(id string) (*SfsTurbo, error) { + sf := &SfsTurbo{region: self} + err := DoGet(self.ecsClient.SfsTurbos.Get, id, nil, &sf) + return sf, errors.Wrapf(err, "self.ecsClient.SfsTurbos.Get") +} + +func (self *SRegion) DeleteSfsTurbo(id string) error { + return DoDelete(self.ecsClient.SfsTurbos.Delete, id, nil, nil) +} + +func (self *SRegion) GetSysDefaultSecgroupId() (string, error) { + secs, err := self.GetSecurityGroups("default", "") + if err != nil { + return "", errors.Wrapf(err, "GetSecurityGroups") + } + if len(secs) > 0 { + return secs[0].ID, nil + } + return "", fmt.Errorf("not found default security group") +} + +func (self *SRegion) CreateICloudFileSystem(opts *cloudprovider.FileSystemCraeteOptions) (cloudprovider.ICloudFileSystem, error) { + fs, err := self.CreateSfsTurbo(opts) + if err != nil { + return nil, errors.Wrapf(err, "CreateSfsTurbo") + } + return fs, nil +} + +func (self *SRegion) CreateSfsTurbo(opts *cloudprovider.FileSystemCraeteOptions) (*SfsTurbo, error) { + secId, err := self.GetSysDefaultSecgroupId() + if err != nil { + return nil, errors.Wrapf(err, "GetSysDefaultSecgroupId") + } + metadata := map[string]string{} + if strings.HasSuffix(opts.StorageType, ".enhanced") { + metadata["expand_type"] = "bandwidth" + } + params := map[string]interface{}{ + "share": map[string]interface{}{ + "name": opts.Name, + "share_proto": strings.ToUpper(opts.Protocol), + "share_type": strings.ToUpper(strings.TrimSuffix(opts.StorageType, ".enhanced")), + "size": opts.Capacity, + "availability_zone": opts.ZoneId, + "vpc_id": opts.VpcId, + "subnet_id": opts.NetworkId, + "security_group_id": secId, + "description": opts.Desc, + "metadata": metadata, + }, + } + resp, err := self.ecsClient.SfsTurbos.Create(jsonutils.Marshal(params)) + if err != nil { + return nil, errors.Wrapf(err, "Create") + } + id, err := resp.GetString("id") + if err != nil { + return nil, errors.Wrapf(err, "resp.GetString(id)") + } + return self.GetSfsTurbo(id) +} + +func (self *SRegion) GetICloudAccessGroups() ([]cloudprovider.ICloudAccessGroup, error) { + return []cloudprovider.ICloudAccessGroup{}, nil +} + +func (self *SRegion) CreateICloudAccessGroup(opts *cloudprovider.SAccessGroup) (cloudprovider.ICloudAccessGroup, error) { + return nil, errors.Wrapf(cloudprovider.ErrNotSupported, "CreateICloudAccessGroup") +} + +func (self *SRegion) GetICloudAccessGroupById(id string) (cloudprovider.ICloudAccessGroup, error) { + return nil, errors.Wrapf(cloudprovider.ErrNotFound, "GetICloudAccessGroupById(%s)", id) +} + +type sMoutTarget struct { + sfs *SfsTurbo +} + +func (self *sMoutTarget) GetName() string { + return self.sfs.Name +} + +func (self *sMoutTarget) GetGlobalId() string { + return self.sfs.GetGlobalId() +} + +func (self *sMoutTarget) GetAccessGroupId() string { + return "" +} + +func (self *sMoutTarget) GetDomainName() string { + return self.sfs.ExportLocation +} + +func (self *sMoutTarget) GetNetworkType() string { + return api.NETWORK_TYPE_VPC +} + +func (self *sMoutTarget) GetNetworkId() string { + return self.sfs.SubnetId +} + +func (self *sMoutTarget) GetVpcId() string { + return self.sfs.VpcId +} + +func (self *sMoutTarget) GetStatus() string { + return api.MOUNT_TARGET_STATUS_AVAILABLE +} + +func (self *sMoutTarget) Delete() error { + return nil +} diff --git a/pkg/multicloud/huawei/shell/filesystem.go b/pkg/multicloud/huawei/shell/filesystem.go new file mode 100644 index 0000000000..fb97501ef2 --- /dev/null +++ b/pkg/multicloud/huawei/shell/filesystem.go @@ -0,0 +1,60 @@ +// 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 shell + +import ( + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud/huawei" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type FileSystemListOptions struct { + } + shellutils.R(&FileSystemListOptions{}, "file-system-list", "List FileSystem", func(cli *huawei.SRegion, args *FileSystemListOptions) error { + sfs, err := cli.GetSfsTurbos() + if err != nil { + return err + } + printList(sfs, 0, 0, 0, []string{}) + return nil + }) + + type FileSystemIdOptions struct { + ID string `help:"File System ID"` + } + shellutils.R(&FileSystemIdOptions{}, "file-system-delete", "Delete filesystem", func(cli *huawei.SRegion, args *FileSystemIdOptions) error { + return cli.DeleteSfsTurbo(args.ID) + }) + + shellutils.R(&FileSystemIdOptions{}, "file-system-show", "Show filesystem", func(cli *huawei.SRegion, args *FileSystemIdOptions) error { + fs, err := cli.GetSfsTurbo(args.ID) + if err != nil { + return err + } + printObject(fs) + return nil + }) + + shellutils.R(&cloudprovider.FileSystemCraeteOptions{}, "file-system-create", "Create filesystem", func(cli *huawei.SRegion, args *cloudprovider.FileSystemCraeteOptions) error { + fs, err := cli.CreateSfsTurbo(args) + if err != nil { + return err + } + printObject(fs) + return nil + }) + +}