From d3ddc7166ef2e753d61f31be7fb86c317783db18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E9=B8=BF=E9=A3=9E?= Date: Fri, 31 May 2024 10:22:02 +0800 Subject: [PATCH] fix(baidu): support baidu resource --- pkg/apis/compute/storage_const.go | 8 + pkg/multicloud/baidu/baidu.go | 206 +++++--- pkg/multicloud/baidu/disk.go | 324 +++++++++++++ pkg/multicloud/baidu/eip.go | 275 +++++++++++ pkg/multicloud/baidu/host.go | 323 +++++++++++++ pkg/multicloud/baidu/image.go | 237 ++++++++++ pkg/multicloud/baidu/instance.go | 550 ++++++++++++++++++++++ pkg/multicloud/baidu/instancenic.go | 97 ++++ pkg/multicloud/baidu/keypair.go | 80 ++++ pkg/multicloud/baidu/monitor.go | 87 ++++ pkg/multicloud/baidu/network.go | 251 ++++++++++ pkg/multicloud/baidu/provider/provider.go | 9 +- pkg/multicloud/baidu/region.go | 281 +++++++++-- pkg/multicloud/baidu/secgroup.go | 214 +++++++++ pkg/multicloud/baidu/secgrouprule.go | 146 ++++++ pkg/multicloud/baidu/shell/disk.go | 49 ++ pkg/multicloud/baidu/shell/eip.go | 62 +++ pkg/multicloud/baidu/shell/image.go | 53 +++ pkg/multicloud/baidu/shell/instances.go | 50 ++ pkg/multicloud/baidu/shell/keypair.go | 35 ++ pkg/multicloud/baidu/shell/monitor.go | 37 ++ pkg/multicloud/baidu/shell/network.go | 69 +++ pkg/multicloud/baidu/shell/printutil.go | 4 +- pkg/multicloud/baidu/shell/region.go | 7 +- pkg/multicloud/baidu/shell/secgroup.go | 63 +++ pkg/multicloud/baidu/shell/snapshot.go | 67 +++ pkg/multicloud/baidu/shell/storage.go | 36 ++ pkg/multicloud/baidu/shell/sts.go | 34 ++ pkg/multicloud/baidu/shell/vpc.go | 61 +++ pkg/multicloud/baidu/shell/zone.go | 35 ++ pkg/multicloud/baidu/signer.go | 8 +- pkg/multicloud/baidu/snapshot.go | 178 +++++++ pkg/multicloud/baidu/snapshotpolicy.go | 207 ++++++++ pkg/multicloud/baidu/storage.go | 176 +++++++ pkg/multicloud/baidu/storagecache.go | 91 ++++ pkg/multicloud/baidu/sts.go | 39 ++ pkg/multicloud/baidu/tag_base.go | 40 ++ pkg/multicloud/baidu/vpcs.go | 198 ++++++++ pkg/multicloud/baidu/wire.go | 98 ++++ pkg/multicloud/baidu/zone.go | 141 ++++++ 40 files changed, 4812 insertions(+), 114 deletions(-) create mode 100644 pkg/multicloud/baidu/disk.go create mode 100644 pkg/multicloud/baidu/eip.go create mode 100644 pkg/multicloud/baidu/host.go create mode 100644 pkg/multicloud/baidu/image.go create mode 100644 pkg/multicloud/baidu/instance.go create mode 100644 pkg/multicloud/baidu/instancenic.go create mode 100644 pkg/multicloud/baidu/keypair.go create mode 100644 pkg/multicloud/baidu/monitor.go create mode 100644 pkg/multicloud/baidu/network.go create mode 100644 pkg/multicloud/baidu/secgroup.go create mode 100644 pkg/multicloud/baidu/secgrouprule.go create mode 100644 pkg/multicloud/baidu/shell/disk.go create mode 100644 pkg/multicloud/baidu/shell/eip.go create mode 100644 pkg/multicloud/baidu/shell/image.go create mode 100644 pkg/multicloud/baidu/shell/instances.go create mode 100644 pkg/multicloud/baidu/shell/keypair.go create mode 100644 pkg/multicloud/baidu/shell/monitor.go create mode 100644 pkg/multicloud/baidu/shell/network.go create mode 100644 pkg/multicloud/baidu/shell/secgroup.go create mode 100644 pkg/multicloud/baidu/shell/snapshot.go create mode 100644 pkg/multicloud/baidu/shell/storage.go create mode 100644 pkg/multicloud/baidu/shell/sts.go create mode 100644 pkg/multicloud/baidu/shell/vpc.go create mode 100644 pkg/multicloud/baidu/shell/zone.go create mode 100644 pkg/multicloud/baidu/snapshot.go create mode 100644 pkg/multicloud/baidu/snapshotpolicy.go create mode 100644 pkg/multicloud/baidu/storage.go create mode 100644 pkg/multicloud/baidu/storagecache.go create mode 100644 pkg/multicloud/baidu/sts.go create mode 100644 pkg/multicloud/baidu/tag_base.go create mode 100644 pkg/multicloud/baidu/vpcs.go create mode 100644 pkg/multicloud/baidu/wire.go create mode 100644 pkg/multicloud/baidu/zone.go diff --git a/pkg/apis/compute/storage_const.go b/pkg/apis/compute/storage_const.go index cf032f77f..705dff7c8 100644 --- a/pkg/apis/compute/storage_const.go +++ b/pkg/apis/compute/storage_const.go @@ -115,6 +115,14 @@ const ( STORAGE_FULL = "full" STORAGE_SYSTEM_FULL = "system_full" + + // baidu storage type + STORAGE_BAIDU_SSD = "ssd" // 通用型SSD + STORAGE_BAIDU_PREMIUM_SSD = "premium_ssd" // 高性能云磁盘 + STORAGE_BAIDU_HDD = "hdd" // 通用型HDD + STORAGE_BAIDU_ENHANCED_SSD_PL1 = "enhanced_ssd_pl1" // 增强型SSD_PL1 + STORAGE_BAIDU_ENHANCED_SSD_PL2 = "enhanced_ssd_pl2" // 增强型SSD_PL2 + STORAGE_BAIDU_ENHANCED_SSD_PL3 = "enhanced_ssd_pl3" // 增强型SSD_PL2 ) const ( diff --git a/pkg/multicloud/baidu/baidu.go b/pkg/multicloud/baidu/baidu.go index 1e1132579..1c13c2420 100644 --- a/pkg/multicloud/baidu/baidu.go +++ b/pkg/multicloud/baidu/baidu.go @@ -25,6 +25,7 @@ import ( "time" "yunion.io/x/jsonutils" + "yunion.io/x/log" "yunion.io/x/pkg/errors" "yunion.io/x/pkg/gotypes" "yunion.io/x/pkg/util/httputils" @@ -37,6 +38,14 @@ const ( CLOUD_PROVIDER_BAIDU_CN = "百度云" BAIDU_DEFAULT_REGION = "bj" ISO8601 = "2006-01-02T15:04:05Z" + + SERVICE_STS = "sts" + SERVICE_BBC = "bbc" + SERVICE_BCC = "bcc" + SERVICE_BOS = "bos" + SERVICE_EIP = "eip" + SERVICE_BCM = "bcm" + SERVICE_BILLING = "billing" ) type BaiduClientConfig struct { @@ -65,14 +74,14 @@ func NewBaiduClientConfig(accessKeyId, accessKeySecret string) *BaiduClientConfi return cfg } -func (self *BaiduClientConfig) Debug(debug bool) *BaiduClientConfig { - self.debug = debug - return self +func (cfg *BaiduClientConfig) Debug(debug bool) *BaiduClientConfig { + cfg.debug = debug + return cfg } -func (self *BaiduClientConfig) CloudproviderConfig(cpcfg cloudprovider.ProviderConfig) *BaiduClientConfig { - self.cpcfg = cpcfg - return self +func (cfg *BaiduClientConfig) CloudproviderConfig(cpcfg cloudprovider.ProviderConfig) *BaiduClientConfig { + cfg.cpcfg = cpcfg + return cfg } func NewBaiduClient(cfg *BaiduClientConfig) (*SBaiduClient, error) { @@ -85,40 +94,54 @@ func NewBaiduClient(cfg *BaiduClientConfig) (*SBaiduClient, error) { return client, err } -func (self *SBaiduClient) GetRegions() []SRegion { +func (cli *SBaiduClient) GetRegions() ([]SRegion, error) { + resp, err := cli.post(SERVICE_BCC, "", "v2/region/describeRegions", nil, nil) + if err != nil { + return nil, err + } ret := []SRegion{} - for k, v := range regions { - ret = append(ret, SRegion{ - client: self, - Region: k, - RegionName: v, - }) + err = resp.Unmarshal(&ret, "regions") + if err != nil { + return nil, err } - return ret + for i := range ret { + ret[i].client = cli + } + return ret, nil } -func (self *SBaiduClient) GetRegion(id string) (*SRegion, error) { - regions := self.GetRegions() +func (cli *SBaiduClient) GetRegion(id string) (*SRegion, error) { + regions, err := cli.GetRegions() + if err != nil { + return nil, err + } for i := range regions { - if regions[i].Region == id { - regions[i].client = self + if regions[i].GetId() == id || regions[i].GetGlobalId() == id { return ®ions[i], nil } } - return nil, cloudprovider.ErrNotFound + return nil, errors.Wrapf(cloudprovider.ErrNotFound, id) } -func (self *SBaiduClient) getUrl(service, regionId, resource string) (string, error) { +func (cli *SBaiduClient) getUrl(service, regionId, resource string) (string, error) { if len(regionId) == 0 { regionId = BAIDU_DEFAULT_REGION } switch service { - case "bbc": + case SERVICE_BBC: return fmt.Sprintf("https://bbc.%s.baidubce.com/%s", regionId, strings.TrimPrefix(resource, "/")), nil - case "bos": + case SERVICE_BCC: + return fmt.Sprintf("https://bcc.%s.baidubce.com/%s", regionId, strings.TrimPrefix(resource, "/")), nil + case SERVICE_BOS: return fmt.Sprintf("https://%s.bcebos.com", regionId), nil - case "billing": + case SERVICE_BILLING: return fmt.Sprintf("https://billing.baidubce.com/%s", strings.TrimPrefix(resource, "/")), nil + case SERVICE_STS: + return fmt.Sprintf("https://sts.bj.baidubce.com/v1/%s", strings.TrimPrefix(resource, "/")), nil + case SERVICE_EIP: + return fmt.Sprintf("https://eip.%s.baidubce.com/%s", regionId, strings.TrimPrefix(resource, "/")), nil + case SERVICE_BCM: + return fmt.Sprintf("http://bcm.%s.baidubce.com/%s", regionId, strings.TrimPrefix(resource, "/")), nil default: return "", errors.Wrapf(cloudprovider.ErrNotSupported, service) } @@ -151,28 +174,35 @@ type sBaiduError struct { RequestId string `json:"requestId"` Code string Message string + method httputils.THttpMethod + url string + body jsonutils.JSONObject } -func (self *sBaiduError) Error() string { - return jsonutils.Marshal(self).String() +func (e *sBaiduError) Error() string { + return jsonutils.Marshal(e).String() } -func (self *sBaiduError) ParseErrorFromJsonResponse(statusCode int, status string, body jsonutils.JSONObject) error { +func (e *sBaiduError) ParseErrorFromJsonResponse(statusCode int, status string, body jsonutils.JSONObject) error { if body != nil { - body.Unmarshal(self) + body.Unmarshal(e) } - self.StatusCode = statusCode - return self + e.StatusCode = statusCode + log.Infof("%s %s body: %s error: %v", e.method, e.url, e.body, e.Error()) + if e.StatusCode == 404 { + return errors.Wrapf(cloudprovider.ErrNotFound, e.Error()) + } + return e } -func (self *SBaiduClient) Do(req *http.Request) (*http.Response, error) { - client := self.getDefaultClient() +func (cli *SBaiduClient) Do(req *http.Request) (*http.Response, error) { + client := cli.getDefaultClient() req.Header.Set("Content-Type", "application/json; charset=utf-8") req.Header.Set("x-bce-date", time.Now().UTC().Format(ISO8601)) req.Header.Set("host", req.Host) - signature, err := self.sign(req) + signature, err := cli.sign(req) if err != nil { return nil, errors.Wrapf(err, "sign") } @@ -181,59 +211,99 @@ func (self *SBaiduClient) Do(req *http.Request) (*http.Response, error) { return client.Do(req) } -func (self *SBaiduClient) list(service, regionId, resource string, params map[string]interface{}) (jsonutils.JSONObject, error) { - return self.request(httputils.GET, service, regionId, resource, params) +func (cli *SBaiduClient) eipList(regionId, resource string, params url.Values) (jsonutils.JSONObject, error) { + return cli.list(SERVICE_EIP, regionId, resource, params) } -func (self *SBaiduClient) post(service, regionId, resource string, params map[string]interface{}) (jsonutils.JSONObject, error) { - return self.request(httputils.POST, service, regionId, resource, params) +func (cli *SBaiduClient) eipPost(regionId, resource string, params url.Values, body map[string]interface{}) (jsonutils.JSONObject, error) { + return cli.post(SERVICE_EIP, regionId, resource, params, body) } -func (self *SBaiduClient) request(method httputils.THttpMethod, service, regionId, resource string, params map[string]interface{}) (jsonutils.JSONObject, error) { - uri, err := self.getUrl(service, regionId, resource) +func (cli *SBaiduClient) eipDelete(regionId, resource string, params url.Values) (jsonutils.JSONObject, error) { + return cli.delete(SERVICE_EIP, regionId, resource, params) +} + +func (cli *SBaiduClient) eipUpdate(regionId, resource string, params url.Values, body map[string]interface{}) (jsonutils.JSONObject, error) { + return cli.update(SERVICE_EIP, regionId, resource, params, body) +} + +func (cli *SBaiduClient) bccList(regionId, resource string, params url.Values) (jsonutils.JSONObject, error) { + return cli.list(SERVICE_BCC, regionId, resource, params) +} + +func (cli *SBaiduClient) bccPost(regionId, resource string, params url.Values, body map[string]interface{}) (jsonutils.JSONObject, error) { + return cli.post(SERVICE_BCC, regionId, resource, params, body) +} + +func (cli *SBaiduClient) bccDelete(regionId, resource string, params url.Values) (jsonutils.JSONObject, error) { + return cli.delete(SERVICE_BCC, regionId, resource, params) +} + +func (cli *SBaiduClient) bccUpdate(regionId, resource string, params url.Values, body map[string]interface{}) (jsonutils.JSONObject, error) { + return cli.update(SERVICE_BCC, regionId, resource, params, body) +} + +func (cli *SBaiduClient) bcmPost(regionId, resource string, params url.Values, body map[string]interface{}) (jsonutils.JSONObject, error) { + return cli.post(SERVICE_BCM, regionId, resource, params, body) +} + +func (cli *SBaiduClient) list(service, regionId, resource string, params url.Values) (jsonutils.JSONObject, error) { + return cli.request(httputils.GET, service, regionId, resource, params, nil) +} + +func (cli *SBaiduClient) update(service, regionId, resource string, params url.Values, body map[string]interface{}) (jsonutils.JSONObject, error) { + return cli.request(httputils.PUT, service, regionId, resource, params, body) +} + +func (cli *SBaiduClient) delete(service, regionId, resource string, params url.Values) (jsonutils.JSONObject, error) { + return cli.request(httputils.DELETE, service, regionId, resource, params, nil) +} + +func (cli *SBaiduClient) post(service, regionId, resource string, params url.Values, body map[string]interface{}) (jsonutils.JSONObject, error) { + return cli.request(httputils.POST, service, regionId, resource, params, body) +} + +func (cli *SBaiduClient) request(method httputils.THttpMethod, service, regionId, resource string, params url.Values, body map[string]interface{}) (jsonutils.JSONObject, error) { + uri, err := cli.getUrl(service, regionId, resource) if err != nil { return nil, err } - if params == nil { - params = map[string]interface{}{} + if body == nil { + body = map[string]interface{}{} } - values := url.Values{} - if method == httputils.GET && len(params) > 0 { - for k, v := range params { - values.Set(k, v.(string)) - } - uri = fmt.Sprintf("%s?%s", uri, values.Encode()) + if len(params) > 0 { + uri = fmt.Sprintf("%s?%s", uri, params.Encode()) } - req := httputils.NewJsonRequest(method, uri, params) - bErr := &sBaiduError{} - client := httputils.NewJsonClient(self) - _, resp, err := client.Send(self.ctx, req, bErr, self.debug) + req := httputils.NewJsonRequest(method, uri, body) + bErr := &sBaiduError{method: method, url: uri, body: jsonutils.Marshal(body)} + client := httputils.NewJsonClient(cli) + _, resp, err := client.Send(cli.ctx, req, bErr, cli.debug) return resp, err } -func (self *SBaiduClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) { +func (cli *SBaiduClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) { subAccount := cloudprovider.SSubAccount{} - subAccount.Id = self.GetAccountId() - subAccount.Name = self.cpcfg.Name - subAccount.Account = self.accessKeyId + subAccount.Id = cli.GetAccountId() + subAccount.Name = cli.cpcfg.Name + subAccount.Account = cli.accessKeyId subAccount.HealthStatus = api.CLOUD_PROVIDER_HEALTH_NORMAL return []cloudprovider.SSubAccount{subAccount}, nil } -func (self *SBaiduClient) getOwnerId() (string, error) { - if len(self.ownerId) > 0 { - return self.ownerId, nil +func (cli *SBaiduClient) getOwnerId() (string, error) { + if len(cli.ownerId) > 0 { + return cli.ownerId, nil } - resp, err := self.list("bos", "bj", "/", nil) + session, err := cli.GetSessionToken() if err != nil { return "", err } - self.ownerId, err = resp.GetString("owner", "id") - return self.ownerId, err + cli.ownerId = session.UserId + return cli.ownerId, nil } -func (self *SBaiduClient) GetAccountId() string { - ownerId, _ := self.getOwnerId() +func (cli *SBaiduClient) GetAccountId() string { + ownerId, _ := cli.getOwnerId() return ownerId } @@ -241,8 +311,8 @@ type CashBalance struct { CashBalance float64 } -func (self *SBaiduClient) QueryBalance() (*CashBalance, error) { - resp, err := self.post("billing", "", "/v1/finance/cash/balance", nil) +func (cli *SBaiduClient) QueryBalance() (*CashBalance, error) { + resp, err := cli.post("billing", "", "/v1/finance/cash/balance", nil, nil) if err != nil { return nil, err } @@ -254,9 +324,13 @@ func (self *SBaiduClient) QueryBalance() (*CashBalance, error) { return ret, nil } -func (self *SBaiduClient) GetCapabilities() []string { +func (cli *SBaiduClient) GetCapabilities() []string { caps := []string{ - cloudprovider.CLOUD_CAPABILITY_COMPUTE + cloudprovider.READ_ONLY_SUFFIX, + cloudprovider.CLOUD_CAPABILITY_COMPUTE, + cloudprovider.CLOUD_CAPABILITY_NETWORK, + cloudprovider.CLOUD_CAPABILITY_SECURITY_GROUP, + cloudprovider.CLOUD_CAPABILITY_EIP, + cloudprovider.CLOUD_CAPABILITY_SNAPSHOT_POLICY, } return caps } diff --git a/pkg/multicloud/baidu/disk.go b/pkg/multicloud/baidu/disk.go new file mode 100644 index 000000000..03935de46 --- /dev/null +++ b/pkg/multicloud/baidu/disk.go @@ -0,0 +1,324 @@ +// 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 baidu + +import ( + "context" + "fmt" + "net/url" + "strings" + "time" + + api "yunion.io/x/cloudmux/pkg/apis/compute" + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/cloudmux/pkg/multicloud" + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/utils" +) + +type Attachment struct { + InstanceId string + MountPoint string + DeleteWithInstance bool +} + +type Attachments struct { + Id string + InstanceId string + Device string + Serial string +} + +type AutoSnapshotPolicy struct { + Id string + Name string + TimePoints []int + RepeatWeekdays []int + RetentionDays int + Status string +} + +type SDisk struct { + storage *SStorage + multicloud.SDisk + SBaiduTag + + Id string + CreateTime time.Time + ExpireTime string + Name string + DiskSizeInGB int + Status string + Type string + StorageType string + Desc string + PaymentTiming string + Attachments []Attachments + RegionId string + SourceSnapshotId string + SnapshotNum string + AutoSnapshotPolicy AutoSnapshotPolicy + ZoneName string + IsSystemVolume bool +} + +func (region *SRegion) GetDisks(storageType, zoneName, instanceId string) ([]SDisk, error) { + params := url.Values{} + if len(zoneName) > 0 { + params.Set("zoneName", zoneName) + } + if len(instanceId) > 0 { + params.Set("instanceId", instanceId) + } + disks := []SDisk{} + for { + resp, err := region.bccList("v2/volume", params) + if err != nil { + return nil, errors.Wrap(err, "list disks") + } + part := struct { + NextMarker string + Volumes []SDisk + }{} + err = resp.Unmarshal(&part) + if err != nil { + return nil, err + } + for i := range part.Volumes { + if len(storageType) == 0 || isMatchStorageType(storageType, part.Volumes[i].StorageType) { + disks = append(disks, part.Volumes[i]) + } + } + if len(part.NextMarker) == 0 { + break + } + params.Set("marker", part.NextMarker) + } + return disks, nil +} + +func (region *SRegion) GetDisk(diskId string) (*SDisk, error) { + resp, err := region.bccList(fmt.Sprintf("v2/volume/%s", diskId), nil) + if err != nil { + return nil, errors.Wrap(err, "list disks") + } + ret := &SDisk{} + err = resp.Unmarshal(ret, "volume") + if err != nil { + return nil, errors.Wrap(err, "unmarshal disks") + } + return ret, nil +} + +func (disk *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) { + return disk.storage, nil +} + +func (disk *SDisk) GetIStorageId() string { + return disk.storage.GetGlobalId() +} + +func (disk *SDisk) GetDiskFormat() string { + return "" +} + +func (disk *SDisk) GetId() string { + return disk.Id +} + +func (disk *SDisk) GetGlobalId() string { + return disk.Id +} + +func (disk *SDisk) GetName() string { + return disk.Name +} + +func (disk *SDisk) GetStatus() string { + switch disk.Status { + case "Available", "InUse", "Recharging": + return api.DISK_READY + case "Detaching": + return api.DISK_DETACHING + case "Error", "NotAvailable", "Expired": + return api.DISK_UNKNOWN + case "Creating": + return api.DISK_ALLOCATING + case "Attaching": + return api.DISK_ATTACHING + case "Deleting": + return api.DISK_DETACHING + case "Scaling": + return api.DISK_RESIZING + case "SnapshotProcessing", "ImageProcessing": + return api.DISK_SAVING + default: + return strings.ToLower(disk.Status) + } +} + +func (disk *SDisk) GetDiskSizeMB() int { + return disk.DiskSizeInGB * 1024 +} + +func (disk *SDisk) GetIsAutoDelete() bool { + return false +} + +func (disk *SDisk) GetTemplateId() string { + return "" +} + +func (disk *SDisk) GetDiskType() string { + if disk.IsSystemVolume { + return api.DISK_TYPE_SYS + } + return api.DISK_TYPE_DATA +} + +func (disk *SDisk) GetFsFormat() string { + return "" +} + +func (disk *SDisk) GetIsNonPersistent() bool { + return false +} + +func (disk *SDisk) GetIops() int { + return 0 +} + +func (disk *SDisk) GetDriver() string { + return "" +} + +func (disk *SDisk) GetCacheMode() string { + return "" +} + +func (disk *SDisk) GetMountpoint() string { + return "" +} + +func (disk *SDisk) GetAccessPath() string { + return "" +} + +func (disk *SDisk) Refresh() error { + res, err := disk.storage.zone.region.GetDisk(disk.Id) + if err != nil { + return err + } + return jsonutils.Update(disk, res) +} + +func (disk *SDisk) Delete(ctx context.Context) error { + return disk.storage.zone.region.DeleteDisk(disk.Id) +} + +func (disk *SDisk) CreateISnapshot(ctx context.Context, name string, desc string) (cloudprovider.ICloudSnapshot, error) { + snapshot, err := disk.storage.zone.region.CreateSnapshot(name, desc, disk.Id) + if err != nil { + return nil, err + } + return snapshot, nil +} + +func (disk *SDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) { + snapshots, err := disk.storage.zone.region.GetSnapshots(disk.Id) + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudSnapshot{} + for i := range snapshots { + snapshots[i].region = disk.storage.zone.region + ret = append(ret, &snapshots[i]) + } + return ret, nil +} + +func (disk *SDisk) GetExtSnapshotPolicyIds() ([]string, error) { + ret := []string{} + if len(disk.AutoSnapshotPolicy.Id) > 0 { + ret = append(ret, disk.AutoSnapshotPolicy.Id) + } + return ret, nil +} + +func (disk *SDisk) Resize(ctx context.Context, newSizeMB int64) error { + return disk.storage.zone.region.ResizeDisk(disk.Id, newSizeMB/1024) +} + +func (disk *SDisk) Reset(ctx context.Context, snapshotId string) (string, error) { + return disk.Id, disk.storage.zone.region.ResetDisk(disk.Id, snapshotId) +} + +func (self *SRegion) ResetDisk(diskId, snapshotId string) error { + params := url.Values{} + params.Set("rollback", "") + body := map[string]interface{}{ + "snapshotId": snapshotId, + } + _, err := self.bccUpdate(fmt.Sprintf("v2/volume/%s", diskId), params, body) + return err +} + +func (disk *SDisk) Rebuild(ctx context.Context) error { + return cloudprovider.ErrNotSupported +} + +func (region *SRegion) DeleteDisk(id string) error { + _, err := region.bccDelete(fmt.Sprintf("v2/volume/%s", id), nil) + return err +} + +func (region *SRegion) CreateDisk(storageType, zoneName string, opts *cloudprovider.DiskCreateConfig) (*SDisk, error) { + params := url.Values{} + params.Set("clientToken", utils.GenRequestId(20)) + body := map[string]interface{}{ + "name": opts.Name, + "description": opts.Desc, + "cdsSizeInGB": opts.SizeGb, + "storageType": storageType, + "zoneName": zoneName, + } + resp, err := region.bccPost("v2/volume", params, body) + if err != nil { + return nil, err + } + ret := struct { + VolumeIds []string + }{} + err = resp.Unmarshal(&ret) + if err != nil { + return nil, err + } + for _, id := range ret.VolumeIds { + return region.GetDisk(id) + } + return nil, errors.Wrapf(cloudprovider.ErrNotFound, resp.String()) +} + +func (region *SRegion) ResizeDisk(diskId string, sizeGb int64) error { + params := url.Values{} + params.Set("clientToken", utils.GenRequestId(20)) + params.Set("resize", "") + body := map[string]interface{}{ + "newCdsSizeInGB": sizeGb, + } + _, err := region.bccUpdate(fmt.Sprintf("v2/volume/%s", diskId), params, body) + return err +} diff --git a/pkg/multicloud/baidu/eip.go b/pkg/multicloud/baidu/eip.go new file mode 100644 index 000000000..5b7d77c00 --- /dev/null +++ b/pkg/multicloud/baidu/eip.go @@ -0,0 +1,275 @@ +// 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 baidu + +import ( + "fmt" + "net/url" + "strings" + "time" + + billing_api "yunion.io/x/cloudmux/pkg/apis/billing" + api "yunion.io/x/cloudmux/pkg/apis/compute" + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/cloudmux/pkg/multicloud" + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/utils" +) + +type SEip struct { + multicloud.SEipBase + SBaiduTag + + region *SRegion + + Name string + Eip string + EipId string + Status string + InstanceType string + InstanceId string + ShareGroupId string + EipInstanceType string + BandwidthInMbps int + PaymentTiming string + BilingMethod string + CreateTime time.Time + ExpireTime time.Time + Region string + RouteTypt string +} + +func (self *SEip) GetId() string { + return self.Eip +} + +func (self *SEip) GetName() string { + return self.Name +} + +func (self *SEip) GetGlobalId() string { + return self.Eip +} + +func (self *SEip) GetStatus() string { + switch self.Status { + case "creating": + return api.EIP_STATUS_ALLOCATE + case "available", "binded", "updating", "paused": + return api.EIP_STATUS_READY + case "binding": + return api.EIP_STATUS_ASSOCIATE + case "unbinding": + return api.EIP_STATUS_DISSOCIATE + case "unavailable": + return api.EIP_STATUS_UNKNOWN + default: + return api.EIP_STATUS_UNKNOWN + } +} + +func (self *SEip) Refresh() error { + eip, err := self.region.GetEip(self.Eip) + if err != nil { + return err + } + return jsonutils.Update(self, eip) +} + +func (self *SEip) GetIpAddr() string { + return self.Eip +} + +func (self *SEip) GetMode() string { + return api.EIP_MODE_STANDALONE_EIP +} + +func (self *SEip) GetAssociationType() string { + switch self.InstanceType { + case "BCC", "BBC", "DCC", "ENI": + return api.EIP_ASSOCIATE_TYPE_SERVER + case "NAT": + return api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY + case "BLB": + return api.EIP_ASSOCIATE_TYPE_LOADBALANCER + default: + return strings.ToLower(self.InstanceType) + } +} + +func (self *SEip) GetAssociationExternalId() string { + return self.InstanceId +} + +func (self *SEip) GetBillingType() string { + if strings.EqualFold(self.PaymentTiming, "prepaid") { + return billing_api.BILLING_TYPE_PREPAID + } + return billing_api.BILLING_TYPE_POSTPAID +} + +func (self *SEip) GetCreatedAt() time.Time { + return self.CreateTime +} + +func (self *SEip) GetExpiredAt() time.Time { + return self.ExpireTime +} + +func (self *SEip) Delete() error { + return self.region.DeleteEip(self.Eip) +} + +func (self *SEip) GetBandwidth() int { + return self.BandwidthInMbps +} + +func (self *SEip) GetInternetChargeType() string { + if strings.EqualFold(self.BilingMethod, "ByTraffic") { + return api.EIP_CHARGE_TYPE_BY_TRAFFIC + } + return api.EIP_CHARGE_TYPE_BY_BANDWIDTH +} + +func (self *SEip) Associate(conf *cloudprovider.AssociateConfig) error { + return self.region.AssociateEip(self.Eip, conf.InstanceId) +} + +func (region *SRegion) AssociateEip(id string, instanceId string) error { + params := url.Values{} + params.Set("clientToken", utils.GenRequestId(20)) + params.Set("bind", "") + body := map[string]interface{}{ + "instanceType": "BCC", + "instanceId": instanceId, + } + _, err := region.eipUpdate(fmt.Sprintf("v1/eip/%s", id), params, body) + return err +} + +func (self *SEip) Dissociate() error { + return self.region.DissociateEip(self.Eip) +} + +func (region *SRegion) DissociateEip(id string) error { + params := url.Values{} + params.Set("clientToken", utils.GenRequestId(20)) + params.Set("unbind", "") + _, err := region.eipUpdate(fmt.Sprintf("v1/eip/%s", id), params, nil) + return err +} + +func (self *SEip) ChangeBandwidth(bw int) error { + return self.region.UpdateEipBandwidth(self.Eip, bw) +} + +func (self *SEip) GetProjectId() string { + return "" +} + +func (region *SRegion) GetEips(instanceId string) ([]SEip, error) { + params := url.Values{} + if len(instanceId) > 0 { + params.Set("instanceId", instanceId) + params.Set("instanceType", "BCC") + } + ret := []SEip{} + for { + resp, err := region.eipList("v1/eip", params) + if err != nil { + return nil, err + } + part := struct { + NextMarker string + EipList []SEip + }{} + err = resp.Unmarshal(&part) + if err != nil { + return nil, err + } + ret = append(ret, part.EipList...) + if len(part.NextMarker) == 0 { + break + } + params.Set("marker", part.NextMarker) + } + return ret, nil +} + +func (region *SRegion) GetEip(id string) (*SEip, error) { + resp, err := region.eipList(fmt.Sprintf("v1/eip/%s", id), nil) + if err != nil { + return nil, err + } + ret := &SEip{region: region} + err = resp.Unmarshal(ret) + if err != nil { + return nil, err + } + return ret, nil +} + +func (region *SRegion) DeleteEip(id string) error { + _, err := region.eipDelete(fmt.Sprintf("v1/eip/%s", id), nil) + return err +} + +func (region *SRegion) UpdateEipBandwidth(id string, bw int) error { + params := url.Values{} + params.Set("clientToken", utils.GenRequestId(20)) + params.Set("resize", "") + body := map[string]interface{}{ + "newBandwidthInMbps": bw, + } + _, err := region.eipUpdate(fmt.Sprintf("v1/eip/%s", id), params, body) + return err +} + +func (region *SRegion) CreateEip(opts *cloudprovider.SEip) (*SEip, error) { + params := url.Values{} + params.Set("clientToken", utils.GenRequestId(20)) + tags := []BaiduTag{} + for k, v := range opts.Tags { + tags = append(tags, BaiduTag{ + TagKey: k, + TagValue: v, + }) + } + billing := map[string]interface{}{ + "paymentTiming": "Postpaid", + "billingMethod": "ByTraffic", + } + if opts.ChargeType == api.EIP_CHARGE_TYPE_BY_BANDWIDTH { + billing["billingMethod"] = "ByBandwidth" + } + body := map[string]interface{}{ + "name": opts.Name, + "bandwidthInMbps": opts.BandwidthMbps, + "tags": tags, + "billing": billing, + } + if len(opts.BGPType) > 0 { + body["routeType"] = opts.BGPType + } + resp, err := region.eipPost("v1/eip", params, body) + if err != nil { + return nil, err + } + eipId, err := resp.GetString("eip") + if err != nil { + return nil, err + } + return region.GetEip(eipId) +} diff --git a/pkg/multicloud/baidu/host.go b/pkg/multicloud/baidu/host.go new file mode 100644 index 000000000..97b73255c --- /dev/null +++ b/pkg/multicloud/baidu/host.go @@ -0,0 +1,323 @@ +// 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 baidu + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "encoding/hex" + "fmt" + "net/url" + + api "yunion.io/x/cloudmux/pkg/apis/compute" + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/cloudmux/pkg/multicloud" + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/utils" +) + +type SHost struct { + multicloud.SHostBase + zone *SZone +} + +func (host *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) { + vms, err := host.zone.region.GetInstances("", []string{}) + if err != nil { + return nil, err + } + ivms := make([]cloudprovider.ICloudVM, len(vms)) + for i := 0; i < len(vms); i += 1 { + vms[i].host = host + ivms[i] = &vms[i] + } + return ivms, nil +} + +func (host *SHost) CreateVM(opts *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) { + vm, err := host.zone.region.CreateInstance(host.zone.ZoneName, opts) + if err != nil { + return nil, err + } + vm.host = host + return vm, nil +} + +func PKCS7Padding(ciphertext []byte, blockSize int) []byte { + padding := blockSize - len(ciphertext)%blockSize + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(ciphertext, padtext...) +} + +type ecb struct { + b cipher.Block + blockSize int +} + +func newECB(b cipher.Block) *ecb { + return &ecb{ + b: b, + blockSize: b.BlockSize(), + } +} + +type ecbEncrypter ecb + +func NewECBEncrypter(b cipher.Block) cipher.BlockMode { + return (*ecbEncrypter)(newECB(b)) +} + +func (x *ecbEncrypter) BlockSize() int { return x.blockSize } + +func (x *ecbEncrypter) CryptBlocks(dst, src []byte) { + if len(src)%x.blockSize != 0 { + panic("crypto/cipher: input not full blocks") + } + if len(dst) < len(src) { + panic("crypto/cipher: output smaller than input") + } + for len(src) > 0 { + x.b.Encrypt(dst, src[:x.blockSize]) + src = src[x.blockSize:] + dst = dst[x.blockSize:] + } +} + +func AesECBEncryptHex(key, message string) (string, error) { + // ECB is left out intentionally because it's insecure, check https://github.com/golang/go/issues/5597 + if len(key) < 16 { + return "", fmt.Errorf("Invalid SecretKey") + } + keyBytes := []byte(key[:16]) + msgBytes := []byte(message) + block, err := aes.NewCipher(keyBytes) + if err != nil { + return "", err + } + blockSize := block.BlockSize() + msgBytes = PKCS7Padding(msgBytes, blockSize) + blockMode := NewECBEncrypter(block) + crypted := make([]byte, len(msgBytes)) + blockMode.CryptBlocks(crypted, msgBytes) + return hex.EncodeToString(crypted), nil +} + +func (region *SRegion) CreateInstance(zoneName string, opts *cloudprovider.SManagedVMCreateConfig) (*SInstance, error) { + params := url.Values{} + params.Set("clientToken", utils.GenRequestId(20)) + tags := []BaiduTag{} + for k, v := range opts.Tags { + tags = append(tags, BaiduTag{ + TagKey: k, + TagValue: v, + }) + } + billing := map[string]interface{}{ + "paymentTiming": "Postpaid", + } + if opts.BillingCycle != nil { + billing["paymentTiming"] = "Prepaid" + reservation := map[string]interface{}{} + if opts.BillingCycle.GetYears() > 0 { + reservation["reservationTimeUnit"] = "year" + reservation["reservationLength"] = opts.BillingCycle.GetYears() + } else if opts.BillingCycle.GetMonths() > 0 { + reservation["reservationTimeUnit"] = "month" + reservation["reservationLength"] = opts.BillingCycle.GetMonths() + } + billing["reservation"] = reservation + } + + disks := []map[string]interface{}{} + for _, disk := range opts.DataDisks { + disks = append(disks, map[string]interface{}{ + "cdsSizeInGB": disk.SizeGB, + "storageType": disk.StorageType, + }) + } + + body := map[string]interface{}{ + "imageId": opts.ExternalImageId, + "spec": opts.InstanceType, + "rootDiskSizeInGb": opts.SysDisk.SizeGB, + "rootDiskStorageType": opts.SysDisk.StorageType, + "networkCapacityInMbps": opts.PublicIpBw, + "name": opts.Name, + "hostname": opts.Hostname, + "adminPass": opts.Password, + "zoneName": zoneName, + "subnetId": opts.ExternalNetworkId, + "tags": tags, + "userData": opts.UserData, + "billing": billing, + "createCdsList": disks, + } + if len(opts.Password) > 0 { + var err error + body["adminPass"], err = AesECBEncryptHex(region.client.accessKeySecret, opts.Password) + if err != nil { + return nil, errors.Wrapf(err, "AesECBEncryptHex") + } + } + + if len(opts.IpAddr) > 0 { + body["internalIps"] = []string{opts.IpAddr} + } + + if len(opts.PublicKey) > 0 { + keypair, err := region.SyncKeypair(opts.KeypairName, opts.PublicKey) + if err != nil { + return nil, err + } + body["keypairId"] = keypair.KeypairId + } + + if len(opts.ExternalSecgroupIds) > 0 { + body["securityGroupId"] = opts.ExternalSecgroupIds[0] + } + resp, err := region.bccPost("v2/instanceBySpec", params, body) + if err != nil { + return nil, err + } + ret := struct { + InstanceIds []string + WarningList []string + }{} + err = resp.Unmarshal(&ret) + if err != nil { + return nil, errors.Wrapf(err, "Unmarshal %s", resp.String()) + } + for _, vmId := range ret.InstanceIds { + return region.GetInstance(vmId) + } + return nil, errors.Wrapf(cloudprovider.ErrNotFound, "after create %s", resp.String()) +} + +func (host *SHost) GetAccessIp() string { + return "" +} + +func (host *SHost) GetAccessMac() string { + return "" +} + +func (host *SHost) GetName() string { + return fmt.Sprintf("%s-%s", host.zone.region.client.cpcfg.Name, host.zone.GetId()) +} + +func (host *SHost) GetNodeCount() int8 { + return 0 +} + +func (host *SHost) GetSN() string { + return "" +} + +func (host *SHost) GetStatus() string { + return api.HOST_STATUS_RUNNING +} + +func (host *SHost) GetCpuCount() int { + return 0 +} + +func (host *SHost) GetCpuDesc() string { + return "" +} + +func (host *SHost) GetCpuMhz() int { + return 0 +} + +func (host *SHost) GetMemSizeMB() int { + return 0 +} + +func (host *SHost) GetStorageSizeMB() int64 { + return 0 +} + +func (host *SHost) GetStorageClass() string { + return "" +} + +func (host *SHost) GetStorageType() string { + return api.DISK_TYPE_HYBRID +} + +func (host *SHost) GetEnabled() bool { + return true +} + +func (host *SHost) GetIsMaintenance() bool { + return false +} + +func (host *SHost) IsEmulated() bool { + return true +} + +func (host *SHost) GetGlobalId() string { + return fmt.Sprintf("%s-%s", host.zone.region.client.cpcfg.Id, host.zone.GetId()) +} + +func (host *SHost) GetId() string { + return fmt.Sprintf("%s-%s", host.zone.region.client.cpcfg.Id, host.zone.GetId()) +} + +func (host *SHost) GetHostStatus() string { + return api.HOST_ONLINE +} + +func (host *SHost) GetHostType() string { + return api.HOST_TYPE_BAIDU +} + +func (host *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) { + wires, err := host.zone.GetIWires() + if err != nil { + return nil, errors.Wrap(err, "GetIWires") + } + return cloudprovider.GetHostNetifs(host, wires), nil +} + +func (host *SHost) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { + return host.zone.GetIStorageById(id) +} + +func (host *SHost) GetIStorages() ([]cloudprovider.ICloudStorage, error) { + return host.zone.GetIStorages() +} + +func (host *SHost) GetIVMById(vmId string) (cloudprovider.ICloudVM, error) { + vm, err := host.zone.region.GetInstance(vmId) + if err != nil { + return nil, err + } + vm.host = host + return vm, nil +} + +func (host *SHost) GetSysInfo() jsonutils.JSONObject { + info := jsonutils.NewDict() + info.Add(jsonutils.NewString(CLOUD_PROVIDER_BAIDU_CN), "manufacture") + return info +} + +func (host *SHost) GetVersion() string { + return "" +} diff --git a/pkg/multicloud/baidu/image.go b/pkg/multicloud/baidu/image.go new file mode 100644 index 000000000..b41116ea2 --- /dev/null +++ b/pkg/multicloud/baidu/image.go @@ -0,0 +1,237 @@ +// 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 baidu + +import ( + "context" + "fmt" + "net/url" + "strings" + "time" + + api "yunion.io/x/cloudmux/pkg/apis/compute" + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/cloudmux/pkg/multicloud" + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/util/imagetools" +) + +type SImage struct { + multicloud.SImageBase + SBaiduTag + + cache *SStoragecache + imgInfo *imagetools.ImageInfo + + Id string + CreateTime time.Time + Name string + Type string + OsType string + OsVersion string + OsLang string + OsName string + OsBuild string + OsArch string + Status string + Desc string + MinDiskGb int64 +} + +func (self *SImage) GetMinRamSizeMb() int { + return 0 +} + +func (self *SImage) GetId() string { + return self.Id +} + +func (self *SImage) GetName() string { + return self.Name +} + +func (self *SImage) Delete(ctx context.Context) error { + return self.cache.region.DeleteImage(self.Id) +} + +func (self *SImage) GetGlobalId() string { + return self.Id +} + +func (self *SImage) GetIStoragecache() cloudprovider.ICloudStoragecache { + return self.cache +} + +func (self *SImage) GetStatus() string { + switch self.Status { + case "Creating": + return api.CACHED_IMAGE_STATUS_SAVING + case "Available": + return api.CACHED_IMAGE_STATUS_ACTIVE + case "CreatedFailed", "NotAvailable", "Error": + return api.CACHED_IMAGE_STATUS_CACHE_FAILED + default: + return strings.ToLower(self.Status) + } +} + +func (self *SImage) GetImageStatus() string { + switch self.Status { + case "Creating": + return cloudprovider.IMAGE_STATUS_QUEUED + case "Available": + return cloudprovider.IMAGE_STATUS_ACTIVE + case "CreatedFailed", "NotAvailable", "Error": + return cloudprovider.IMAGE_STATUS_DELETED + default: + return cloudprovider.IMAGE_STATUS_KILLED + } +} + +func (self *SImage) Refresh() error { + image, err := self.cache.region.GetImage(self.Id) + if err != nil { + return err + } + return jsonutils.Update(self, image) +} + +func (self *SImage) GetImageType() cloudprovider.TImageType { + switch self.Type { + case "System": + return cloudprovider.ImageTypeSystem + case "Custom": + return cloudprovider.ImageTypeCustomized + case "Integration": + return cloudprovider.ImageTypeMarket + default: + return cloudprovider.ImageTypeCustomized + } +} + +func (self *SImage) GetSizeByte() int64 { + return int64(self.GetMinOsDiskSizeGb()) * 1024 * 1024 * 1024 +} + +func (self *SImage) GetOsType() cloudprovider.TOsType { + return cloudprovider.TOsType(self.getNormalizedImageInfo().OsType) +} + +func (self *SImage) GetOsDist() string { + return self.getNormalizedImageInfo().OsDistro +} + +func getOsArch(osArch string) string { + for _, key := range []string{"x86_64", "i386", "amd64", "aarch64"} { + if strings.Contains(osArch, key) { + return key + } + } + return osArch +} + +func (self *SImage) getNormalizedImageInfo() *imagetools.ImageInfo { + if self.imgInfo == nil { + imgInfo := imagetools.NormalizeImageInfo(self.OsName, getOsArch(self.OsArch), self.OsType, self.OsName, self.OsVersion) + self.imgInfo = &imgInfo + } + + return self.imgInfo +} + +func (self *SImage) GetFullOsName() string { + return self.Name +} + +func (self *SImage) GetOsVersion() string { + return self.getNormalizedImageInfo().OsVersion +} + +func (self *SImage) GetOsLang() string { + return self.getNormalizedImageInfo().OsLang +} + +func (self *SImage) GetOsArch() string { + return self.getNormalizedImageInfo().OsArch +} + +func (self *SImage) GetBios() cloudprovider.TBiosType { + return cloudprovider.ToBiosType(self.getNormalizedImageInfo().OsBios) +} + +func (self *SImage) GetMinOsDiskSizeGb() int { + if self.MinDiskGb > 0 { + return int(self.MinDiskGb) + } + image, err := self.cache.region.GetImage(self.Id) + if err != nil { + if strings.EqualFold(self.OsType, "windows") { + return 40 + } + return 20 + } + return int(image.MinDiskGb) +} + +func (self *SImage) GetImageFormat() string { + return "raw" +} + +func (self *SImage) GetCreatedAt() time.Time { + return self.CreateTime +} + +func (region *SRegion) GetImages(imageType string) ([]SImage, error) { + params := url.Values{} + if len(imageType) > 0 { + params.Set("imageType", imageType) + } + ret := []SImage{} + for { + resp, err := region.bccList("v2/image", params) + if err != nil { + return nil, err + } + part := struct { + NextMarker string + Images []SImage + }{} + err = resp.Unmarshal(&part) + ret = append(ret, part.Images...) + if len(part.NextMarker) == 0 { + break + } + params.Set("marker", part.NextMarker) + } + return ret, nil +} + +func (region *SRegion) GetImage(id string) (*SImage, error) { + resp, err := region.bccList(fmt.Sprintf("v2/image/%s", id), nil) + if err != nil { + return nil, err + } + ret := &SImage{} + err = resp.Unmarshal(ret, "image") + if err != nil { + return nil, err + } + return ret, nil +} + +func (region *SRegion) DeleteImage(id string) error { + _, err := region.bccDelete(fmt.Sprintf("v2/image/%s", id), nil) + return err +} diff --git a/pkg/multicloud/baidu/instance.go b/pkg/multicloud/baidu/instance.go new file mode 100644 index 000000000..d98c582f0 --- /dev/null +++ b/pkg/multicloud/baidu/instance.go @@ -0,0 +1,550 @@ +// 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 baidu + +import ( + "context" + "fmt" + "net/url" + "strings" + "time" + + api "yunion.io/x/cloudmux/pkg/apis/compute" + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/cloudmux/pkg/multicloud" + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/utils" +) + +type SInstance struct { + multicloud.SInstanceBase + SBaiduTag + host *SHost + + Id string + Name string + RoleName string + Hostname string + InstanceType string + Spec string + Status string + Desc string + CreatedFrom string + PaymentTiming string + CreateTime time.Time + InternalIP string + PublicIP string + CPUCount int + IsomerismCard string + CardCount string + NpuVideoMemory string + MemoryCapacityInGB int + LocalDiskSizeInGB int + ImageId string + ImageName string + ImageType string + PlacementPolicy string + SubnetId string + VpcId string + HostId string + SwitchId string + RackId string + DeploysetId string + ZoneName string + DedicatedHostId string + OsVersion string + OsArch string + OsName string + HosteyeType string + NicInfo NicInfo + DeletionProtection int + Ipv6 string + Volumes []SDisk + NetworkCapacityInMbps int +} + +func (region *SRegion) GetInstances(zoneName string, instanceIds []string) ([]SInstance, error) { + params := url.Values{} + if len(zoneName) > 0 { + params.Set("zoneName", zoneName) + } + if len(instanceIds) > 0 { + params.Set("instanceIds", strings.Join(instanceIds, ",")) + } + ret := []SInstance{} + for { + resp, err := region.bccList("v2/instance", params) + if err != nil { + return nil, errors.Wrap(err, "list instance") + } + part := struct { + NextMarker string + Instances []SInstance + }{} + err = resp.Unmarshal(&part) + if err != nil { + return nil, err + } + ret = append(ret, part.Instances...) + if len(part.NextMarker) == 0 { + break + } + params.Set("marker", part.NextMarker) + } + return ret, nil +} + +func (region *SRegion) GetInstance(instanceId string) (*SInstance, error) { + resp, err := region.bccList(fmt.Sprintf("v2/instance/%s", instanceId), nil) + if err != nil { + return nil, errors.Wrap(err, "show instance") + } + ret := &SInstance{} + err = resp.Unmarshal(ret, "instance") + if err != nil { + return nil, errors.Wrap(err, "unmarshal instance") + } + return ret, nil +} + +func (ins *SInstance) AssignSecurityGroup(secgroupId string) error { + return ins.host.zone.region.AssignSecurityGroup(ins.Id, secgroupId) +} + +func (region *SRegion) AssignSecurityGroup(vmId string, secgroupId string) error { + params := url.Values{} + params.Set("bind", "") + body := map[string]interface{}{ + "securityGroupId": secgroupId, + } + _, err := region.bccUpdate(fmt.Sprintf("v2/instance/%s", vmId), params, body) + return err +} + +func (region *SRegion) RevokeSecurityGroup(vmId string, secgroupId string) error { + params := url.Values{} + params.Set("unbind", "") + body := map[string]interface{}{ + "securityGroupId": secgroupId, + } + _, err := region.bccUpdate(fmt.Sprintf("v2/instance/%s", vmId), params, body) + return err +} + +func (ins *SInstance) AttachDisk(ctx context.Context, diskId string) error { + return ins.host.zone.region.AttachDisk(ins.Id, diskId) +} + +func (region *SRegion) AttachDisk(vmId string, diskId string) error { + params := url.Values{} + params.Set("attach", "") + body := map[string]interface{}{ + "instanceId": vmId, + } + _, err := region.bccUpdate(fmt.Sprintf("v2/volume/%s", diskId), params, body) + return err +} + +func (ins *SInstance) ChangeConfig(ctx context.Context, opts *cloudprovider.SManagedVMChangeConfig) error { + return ins.host.zone.region.ChangeVmConfig(ins.Id, opts.InstanceType) +} + +func (region *SRegion) ChangeVmConfig(vmId string, instanceType string) error { + params := url.Values{} + params.Set("clientToken", utils.GenRequestId(20)) + params.Set("resize", "") + body := map[string]interface{}{ + "spec": instanceType, + } + _, err := region.bccUpdate(fmt.Sprintf("v2/instance/%s", vmId), params, body) + return err +} + +func (ins *SInstance) DeleteVM(ctx context.Context) error { + return ins.host.zone.region.DeleteVm(ins.Id) +} + +func (ins *SInstance) Refresh() error { + vm, err := ins.host.zone.region.GetInstance(ins.Id) + if err != nil { + return err + } + return jsonutils.Update(ins, vm) +} + +func (region *SRegion) DeleteVm(id string) error { + params := url.Values{} + body := map[string]interface{}{ + "relatedReleaseFlag": true, + "deleteCdsSnapshotFlag": true, + "deleteRelatedEnisFlag": true, + "bccRecycleFlag": false, + } + _, err := region.bccPost(fmt.Sprintf("v2/instance/%s", id), params, body) + return err +} + +func (ins *SInstance) DetachDisk(ctx context.Context, diskId string) error { + return ins.host.zone.region.DetachDisk(ins.Id, diskId) +} + +func (region *SRegion) DetachDisk(vmId string, diskId string) error { + params := url.Values{} + params.Set("detach", "") + body := map[string]interface{}{ + "instanceId": vmId, + } + _, err := region.bccUpdate(fmt.Sprintf("v2/volume/%s", diskId), params, body) + return err +} + +func (ins *SInstance) GetBios() cloudprovider.TBiosType { + return "" +} + +func (ins *SInstance) GetBootOrder() string { + return "bcd" +} + +func (ins *SInstance) GetError() error { + return nil +} + +func (ins *SInstance) GetFullOsName() string { + return "" +} + +func (ins *SInstance) GetGlobalId() string { + return ins.Id +} + +func (ins *SInstance) GetId() string { + return ins.Id +} + +func (ins *SInstance) GetInstanceType() string { + return ins.Spec +} + +func (ins *SInstance) GetMachine() string { + return "pc" +} + +func (ins *SInstance) GetHostname() string { + return ins.Hostname +} + +func (ins *SInstance) GetName() string { + return ins.Name +} + +func (ins *SInstance) GetOsArch() string { + return getOsArch(ins.OsArch) +} + +func (ins *SInstance) GetOsDist() string { + return "" +} + +func (ins *SInstance) GetOsLang() string { + return ins.OsName +} + +func (ins *SInstance) GetOsType() cloudprovider.TOsType { + if strings.Contains(strings.ToLower(ins.OsName), "windows") { + return cloudprovider.OsTypeWindows + } + return cloudprovider.OsTypeLinux +} + +func (ins *SInstance) GetOsVersion() string { + return ins.OsVersion +} + +func (ins *SInstance) GetProjectId() string { + return "" +} + +func (ins *SInstance) GetSecurityGroupIds() ([]string, error) { + return ins.NicInfo.SecurityGroups, nil +} + +func (ins *SInstance) GetStatus() string { + switch ins.Status { + case "Running": + return api.VM_RUNNING + case "Stopped": + return api.VM_READY + case "Stopping": + return api.VM_STOPPING + case "Starting": + return api.VM_STARTING + default: + return strings.ToLower(ins.Status) + } +} + +func (ins *SInstance) GetHypervisor() string { + return api.HYPERVISOR_BAIDU +} + +func (ins *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) { + disks, err := ins.host.zone.region.GetDisks("", "", ins.Id) + if err != nil { + return nil, err + } + storages, err := ins.host.zone.region.GetStorageTypes(ins.host.zone.ZoneName) + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudDisk{} + for i := range disks { + for j := range storages { + if isMatchStorageType(disks[i].StorageType, storages[j].StorageType) { + storages[i].zone = ins.host.zone + disks[i].storage = &storages[i] + ret = append(ret, &disks[i]) + break + } + } + } + return ret, nil +} + +func (ins *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) { + eips, err := ins.host.zone.region.GetEips(ins.Id) + if err != nil { + return nil, err + } + for i := range eips { + eips[i].region = ins.host.zone.region + return &eips[i], nil + } + return nil, cloudprovider.ErrNotFound +} + +func (ins *SInstance) GetINics() ([]cloudprovider.ICloudNic, error) { + return []cloudprovider.ICloudNic{&ins.NicInfo}, nil +} + +func (ins *SInstance) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) { + url, err := ins.host.zone.region.GetInstanceVnc(ins.Id) + if err != nil { + return nil, err + } + return &cloudprovider.ServerVncOutput{ + Url: url, + Protocol: "baidu", + InstanceId: ins.Id, + Hypervisor: api.HYPERVISOR_BAIDU, + OsName: string(ins.GetOsType()), + }, nil +} + +func (ins *SInstance) GetVcpuCount() int { + return ins.CPUCount +} + +func (ins *SInstance) GetVmemSizeMB() int { + return ins.MemoryCapacityInGB * 1024 +} + +func (ins *SInstance) GetVdi() string { + return "" +} + +func (ins *SInstance) GetVga() string { + return "" +} + +func (ins *SInstance) RebuildRoot(ctx context.Context, opts *cloudprovider.SManagedVMRebuildRootConfig) (string, error) { + disks, err := ins.host.zone.region.GetDisks("", "", ins.Id) + if err != nil { + return "", err + } + systemDiskId := "" + for _, disk := range disks { + if disk.IsSystemVolume { + systemDiskId = disk.Id + } + } + err = ins.host.zone.region.RebuildRoot(ins.Id, opts) + if err != nil { + return "", err + } + return systemDiskId, nil +} + +func (region *SRegion) RebuildRoot(vmId string, opts *cloudprovider.SManagedVMRebuildRootConfig) error { + params := url.Values{} + params.Set("rebuild", "") + body := map[string]interface{}{ + "imageId": opts.ImageId, + "userData": opts.UserData, + } + var err error + if len(opts.PublicKey) > 0 { + keypair, err := region.SyncKeypair(fmt.Sprintf("auto-generate-%d", time.Now().Unix()), opts.PublicKey) + if err != nil { + return err + } + body["keypairId"] = keypair.KeypairId + } else if len(opts.Password) > 0 { + body["adminPass"], err = AesECBEncryptHex(region.client.accessKeySecret, opts.Password) + if err != nil { + return err + } + } + _, err = region.bccUpdate(fmt.Sprintf("v2/instance/%s", vmId), params, body) + return err +} + +func (ins *SInstance) SetSecurityGroups(secgroupIds []string) error { + current, err := ins.GetSecurityGroupIds() + if err != nil { + return errors.Wrapf(err, "GetSecurityGroupIds") + } + for _, secgroupId := range secgroupIds { + if !utils.IsInStringArray(secgroupId, current) { + err = ins.host.zone.region.AssignSecurityGroup(ins.Id, secgroupId) + if err != nil { + return errors.Wrapf(err, "AssignSecurityGroup %s", secgroupId) + } + } + } + for _, secgroupId := range current { + if !utils.IsInStringArray(secgroupId, secgroupIds) { + err = ins.host.zone.region.RevokeSecurityGroup(ins.Id, secgroupId) + if err != nil { + return errors.Wrapf(err, "RevokeSecurityGroup %s", secgroupId) + } + } + } + return nil +} + +func (ins *SInstance) StartVM(ctx context.Context) error { + return ins.host.zone.region.StartVM(ins.Id) +} + +func (region *SRegion) StartVM(vmId string) error { + params := url.Values{} + params.Set("start", "") + _, err := region.bccUpdate(fmt.Sprintf("v2/instance/%s", vmId), params, nil) + return err +} + +func (ins *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error { + return ins.host.zone.region.StopVM(ins.Id, opts) +} + +func (region *SRegion) StopVM(vmId string, opts *cloudprovider.ServerStopOptions) error { + params := url.Values{} + params.Set("stop", "") + body := map[string]interface{}{ + "forceStop": opts.IsForce, + "stopWithNoCharge": opts.StopCharging, + } + _, err := region.bccUpdate(fmt.Sprintf("v2/instance/%s", vmId), params, body) + return err +} + +func (ins *SInstance) UpdateUserData(userData string) error { + return cloudprovider.ErrNotSupported +} + +func (ins *SInstance) UpdateVM(ctx context.Context, input cloudprovider.SInstanceUpdateOptions) error { + if len(input.HostName) > 0 { + err := ins.host.zone.region.UpdateVmHostname(ins.Id, input.HostName) + if err != nil { + return err + } + } + if len(input.Description) > 0 { + err := ins.host.zone.region.UpdateVmDesc(ins.Id, input.Description) + if err != nil { + return err + } + } + if len(input.NAME) > 0 { + err := ins.host.zone.region.UpdateVmAttr(ins.Id, input.NAME) + if err != nil { + return err + } + } + return nil +} + +func (region *SRegion) UpdateVmAttr(vmId string, name string) error { + params := url.Values{} + params.Set("modifyAttribute", "") + body := map[string]interface{}{ + "name": name, + } + _, err := region.bccUpdate(fmt.Sprintf("v2/instance/%s", vmId), params, body) + return err +} + +func (region *SRegion) UpdateVmDesc(vmId string, desc string) error { + params := url.Values{} + params.Set("modifyDesc", "") + body := map[string]interface{}{ + "desc": desc, + } + _, err := region.bccUpdate(fmt.Sprintf("v2/instance/%s", vmId), params, body) + return err +} + +func (region *SRegion) UpdateVmHostname(vmId string, hostname string) error { + params := url.Values{} + params.Set("changeHostname", "") + body := map[string]interface{}{ + "reboot": false, + "hostname": hostname, + } + _, err := region.bccUpdate(fmt.Sprintf("v2/instance/%s", vmId), params, body) + return err +} + +func (ins *SInstance) GetIHost() cloudprovider.ICloudHost { + return ins.host +} + +func (region *SRegion) UpdateVmPassword(vmId string, passwd string) error { + params := url.Values{} + params.Set("changePass", "") + body := map[string]interface{}{ + "adminPass": passwd, + } + _, err := region.bccUpdate(fmt.Sprintf("v2/instance/%s", vmId), params, body) + return err +} + +func (ins *SInstance) DeployVM(ctx context.Context, opts *cloudprovider.SInstanceDeployOptions) error { + if len(opts.Password) > 0 { + return ins.host.zone.region.UpdateVmPassword(ins.Id, opts.Password) + } + return nil +} + +func (region *SRegion) GetInstanceVnc(vmId string) (string, error) { + params := url.Values{} + resp, err := region.bccList(fmt.Sprintf("v2/instance/%s/vnc", vmId), params) + if err != nil { + return "", err + } + return resp.GetString("vncUrl") +} diff --git a/pkg/multicloud/baidu/instancenic.go b/pkg/multicloud/baidu/instancenic.go new file mode 100644 index 000000000..97b6f34b5 --- /dev/null +++ b/pkg/multicloud/baidu/instancenic.go @@ -0,0 +1,97 @@ +// 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 baidu + +import "yunion.io/x/cloudmux/pkg/cloudprovider" + +type Ips struct { + PrivateIP string + Eip string + Primary bool + EipId string + EipAllocationId string + EipSize string + EipStatus string + EipGroupId string + EipType string +} + +type NicInfo struct { + cloudprovider.DummyICloudNic + + EniId string + EniUUId string + Name string + Type string + SubnetId string + SubnetType string + Az string + Description string + DeviceId string + Status string + MacAddress string + VpcId string + CreatedTime string + EniNum int + EriNum int + Ips []Ips + SecurityGroups []string +} + +func (nic *NicInfo) GetId() string { + return nic.EniId +} + +func (nic *NicInfo) GetIP() string { + for _, ip := range nic.Ips { + if ip.Primary { + return ip.PrivateIP + } + } + return "" +} + +func (nic *NicInfo) InClassicNetwork() bool { + return false +} + +func (nic *NicInfo) GetIP6() string { + return "" +} + +func (nic *NicInfo) GetDriver() string { + return "virtio" +} + +func (nic *NicInfo) GetINetworkId() string { + return nic.SubnetId +} + +func (nic *NicInfo) GetMAC() string { + return nic.MacAddress +} + +func (nic *NicInfo) GetSubAddress() ([]string, error) { + ret := []string{} + for _, ip := range nic.Ips { + if ip.Primary { + continue + } + if len(ip.PrivateIP) > 0 { + ret = append(ret, ip.PrivateIP) + } + } + return ret, nil +} diff --git a/pkg/multicloud/baidu/keypair.go b/pkg/multicloud/baidu/keypair.go new file mode 100644 index 000000000..b20aa62f4 --- /dev/null +++ b/pkg/multicloud/baidu/keypair.go @@ -0,0 +1,80 @@ +// 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 baidu + +import "net/url" + +type SKeypair struct { + Name string + PublicKey string + FingerPrint string + RegionId string + KeypairId string +} + +func (self *SRegion) GetKeypairs() ([]SKeypair, error) { + params := url.Values{} + ret := []SKeypair{} + for { + resp, err := self.bccList("v2/keypair", params) + if err != nil { + return nil, err + } + part := struct { + Keypairs []SKeypair + NextMarker string + }{} + err = resp.Unmarshal(&part) + if err != nil { + return nil, err + } + ret = append(ret, part.Keypairs...) + if len(part.NextMarker) == 0 { + break + } + params.Set("marker", part.NextMarker) + } + return ret, nil +} + +func (self *SRegion) SyncKeypair(name, publicKey string) (*SKeypair, error) { + keypairs, err := self.GetKeypairs() + if err != nil { + return nil, err + } + for i := range keypairs { + if keypairs[i].PublicKey == publicKey { + return &keypairs[i], nil + } + } + return self.CreateKeypair(name, publicKey) +} + +func (self *SRegion) CreateKeypair(name, publicKey string) (*SKeypair, error) { + body := map[string]interface{}{ + "name": name, + "publicKey": publicKey, + } + resp, err := self.bccPost("v2/keypair", nil, body) + if err != nil { + return nil, err + } + ret := &SKeypair{} + err = resp.Unmarshal(ret, "keypair") + if err != nil { + return nil, err + } + return ret, nil +} diff --git a/pkg/multicloud/baidu/monitor.go b/pkg/multicloud/baidu/monitor.go new file mode 100644 index 000000000..0c0953cb7 --- /dev/null +++ b/pkg/multicloud/baidu/monitor.go @@ -0,0 +1,87 @@ +// 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 baidu + +import ( + "fmt" + "net/url" + "strings" + "time" + + "yunion.io/x/pkg/errors" + + "yunion.io/x/cloudmux/pkg/cloudprovider" +) + +func (self *SBaiduClient) GetMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) { + switch opts.ResourceType { + case cloudprovider.METRIC_RESOURCE_TYPE_SERVER: + return self.GetEcsMetrics(opts) + default: + return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "%s", opts.ResourceType) + } +} + +type SMetric struct { + Average float64 + Timestamp time.Time +} + +func (self *SBaiduClient) GetEcsMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) { + ret := []cloudprovider.MetricValues{} + metricNames := map[cloudprovider.TMetricType]string{ + cloudprovider.VM_METRIC_TYPE_CPU_USAGE: "CPUUsagePercent", + cloudprovider.VM_METRIC_TYPE_MEM_USAGE: "MemUsedPercent", + cloudprovider.VM_METRIC_TYPE_DISK_USAGE: "RootUsedPercent", + cloudprovider.VM_METRIC_TYPE_NET_BPS_RX: "VNicInBPS", + cloudprovider.VM_METRIC_TYPE_NET_BPS_TX: "VNicOutBPS", + } + if strings.EqualFold(opts.OsType, "windows") { + metricNames[cloudprovider.VM_METRIC_TYPE_DISK_USAGE] = "DiskCUsedPercent" + } + metricName, ok := metricNames[opts.MetricType] + if !ok { + return nil, errors.Wrapf(cloudprovider.ErrNotSupported, "metric type %s", opts.MetricType) + } + res := fmt.Sprintf("json-api/v1/metricdata/%s/BCE_BCC/%s", self.ownerId, metricName) + params := url.Values{} + params.Set("dimensions", fmt.Sprintf("InstanceId:%s", opts.ResourceId)) + params.Set("statistics[]", "average") + params.Set("startTime", opts.StartTime.UTC().Format(time.RFC3339)) + params.Set("endTime", opts.EndTime.UTC().Format(time.RFC3339)) + params.Set("periodInSecond", "60") + resp, err := self.list(SERVICE_BCM, opts.RegionExtId, res, params) + if err != nil { + return nil, err + } + metrics := []SMetric{} + err = resp.Unmarshal(&metrics, "dataPoints") + if err != nil { + return nil, err + } + metric := cloudprovider.MetricValues{ + Id: opts.ResourceId, + MetricType: opts.MetricType, + Values: []cloudprovider.MetricValue{}, + } + for _, v := range metrics { + metric.Values = append(metric.Values, cloudprovider.MetricValue{ + Timestamp: v.Timestamp, + Value: v.Average, + }) + } + ret = append(ret, metric) + return ret, nil +} diff --git a/pkg/multicloud/baidu/network.go b/pkg/multicloud/baidu/network.go new file mode 100644 index 000000000..cb71244e0 --- /dev/null +++ b/pkg/multicloud/baidu/network.go @@ -0,0 +1,251 @@ +// 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 baidu + +import ( + "fmt" + "net/url" + "time" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/util/netutils" + "yunion.io/x/pkg/util/rbacscope" + "yunion.io/x/pkg/utils" + + api "yunion.io/x/cloudmux/pkg/apis/compute" + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/cloudmux/pkg/multicloud" +) + +type SNetwork struct { + multicloud.SNetworkBase + SBaiduTag + wire *SWire + + Name string + SubnetId string + ZoneName string + CIDR string + VpcId string + SubnetType string + + Description string + + IPv6Cidr string + CreatedTime time.Time +} + +func (self *SNetwork) GetId() string { + return self.SubnetId +} + +func (self *SNetwork) GetName() string { + if len(self.Name) > 0 { + return self.Name + } + return self.SubnetId +} + +func (self *SNetwork) GetGlobalId() string { + return self.SubnetId +} + +func (self *SNetwork) GetStatus() string { + return api.NETWORK_STATUS_AVAILABLE +} + +func (self *SNetwork) Refresh() error { + net, err := self.wire.vpc.region.GetNetwork(self.SubnetId) + if err != nil { + return err + } + return jsonutils.Update(self, net) +} + +func (self *SNetwork) GetIWire() cloudprovider.ICloudWire { + return self.wire +} + +func (net *SNetwork) GetIp6Start() string { + if len(net.IPv6Cidr) > 0 { + prefix, err := netutils.NewIPV6Prefix(net.IPv6Cidr) + if err != nil { + return "" + } + return prefix.Address.NetAddr(prefix.MaskLen).StepUp().String() + } + return "" +} + +func (net *SNetwork) GetIp6End() string { + if len(net.IPv6Cidr) > 0 { + prefix, err := netutils.NewIPV6Prefix(net.IPv6Cidr) + if err != nil { + return "" + } + end := prefix.Address.NetAddr(prefix.MaskLen).BroadcastAddr(prefix.MaskLen) + for i := 0; i < 15; i++ { + end = end.StepDown() + } + return end.String() + } + return "" +} + +func (net *SNetwork) GetIp6Mask() uint8 { + if len(net.IPv6Cidr) > 0 { + prefix, err := netutils.NewIPV6Prefix(net.IPv6Cidr) + if err != nil { + return 0 + } + return prefix.MaskLen + } + return 0 +} + +func (net *SNetwork) GetGateway6() string { + if len(net.IPv6Cidr) > 0 { + prefix, err := netutils.NewIPV6Prefix(net.IPv6Cidr) + if err != nil { + return "" + } + return prefix.Address.NetAddr(prefix.MaskLen).StepUp().String() + } + return "" +} + +func (self *SNetwork) GetIpStart() string { + pref, _ := netutils.NewIPV4Prefix(self.CIDR) + startIp := pref.Address.NetAddr(pref.MaskLen) // 0 + startIp = startIp.StepUp() // 1 + return startIp.String() +} + +func (self *SNetwork) GetIpEnd() string { + pref, _ := netutils.NewIPV4Prefix(self.CIDR) + endIp := pref.Address.BroadcastAddr(pref.MaskLen) // 255 + endIp = endIp.StepDown() // 254 + return endIp.String() +} + +func (self *SNetwork) GetIpMask() int8 { + pref, _ := netutils.NewIPV4Prefix(self.CIDR) + return pref.MaskLen +} + +func (self *SNetwork) GetGateway() string { + pref, _ := netutils.NewIPV4Prefix(self.CIDR) + endIp := pref.Address.BroadcastAddr(pref.MaskLen) // 255 + endIp = endIp.StepDown() // 254 + return endIp.String() +} + +func (self *SNetwork) GetServerType() string { + return api.NETWORK_TYPE_GUEST +} + +func (self *SNetwork) GetIsPublic() bool { + return true +} + +func (self *SNetwork) GetProjectId() string { + return "" +} + +func (self *SNetwork) GetPublicScope() rbacscope.TRbacScope { + return rbacscope.ScopeDomain +} + +func (self *SRegion) DeleteNetwork(id string) error { + params := url.Values{} + params.Set("clientToken", utils.GenRequestId(20)) + _, err := self.bccDelete(fmt.Sprintf("v1/subnet/%s", id), params) + return err +} + +func (self *SNetwork) Delete() error { + return self.wire.vpc.region.DeleteNetwork(self.SubnetId) +} + +func (self *SNetwork) GetAllocTimeoutSeconds() int { + return 120 // 2 minutes +} + +func (self *SRegion) GetNetworks(vpcId, zoneName string) ([]SNetwork, error) { + params := url.Values{} + if len(vpcId) > 0 { + params.Set("vpcId", vpcId) + } + if len(zoneName) > 0 { + params.Set("zoneName", zoneName) + } + ret := []SNetwork{} + for { + resp, err := self.bccList("v1/subnet", params) + if err != nil { + return nil, err + } + part := struct { + NextMarker string + Subnets []SNetwork + }{} + err = resp.Unmarshal(&part) + if err != nil { + return nil, errors.Wrapf(err, "Unmarshal") + } + ret = append(ret, part.Subnets...) + if len(part.NextMarker) == 0 { + break + } + params.Set("marker", part.NextMarker) + } + return ret, nil +} + +func (self *SRegion) GetNetwork(id string) (*SNetwork, error) { + res := fmt.Sprintf("v1/subnet/%s", id) + resp, err := self.bccList(res, nil) + if err != nil { + return nil, err + } + ret := &SNetwork{} + err = resp.Unmarshal(ret, "subnet") + if err != nil { + return nil, errors.Wrapf(err, "Unmarshal") + } + return ret, nil +} + +func (self *SRegion) CreateNetwork(zoneName, vpcId string, opts *cloudprovider.SNetworkCreateOptions) (*SNetwork, error) { + params := url.Values{} + params.Set("clientToken", utils.GenRequestId(20)) + body := map[string]interface{}{ + "zoneName": zoneName, + "name": opts.Name, + "description": opts.Desc, + "cidr": opts.Cidr, + "vpcId": vpcId, + } + resp, err := self.bccPost("v1/subnet", params, body) + if err != nil { + return nil, err + } + subnetId, err := resp.GetString("subnetId") + if err != nil { + return nil, err + } + return self.GetNetwork(subnetId) +} diff --git a/pkg/multicloud/baidu/provider/provider.go b/pkg/multicloud/baidu/provider/provider.go index 746934a0b..cb8b44886 100644 --- a/pkg/multicloud/baidu/provider/provider.go +++ b/pkg/multicloud/baidu/provider/provider.go @@ -102,7 +102,7 @@ type SBaiduProvider struct { } func (self *SBaiduProvider) GetSysInfo() (jsonutils.JSONObject, error) { - regions := self.client.GetRegions() + regions, _ := self.client.GetRegions() info := jsonutils.NewDict() info.Add(jsonutils.NewInt(int64(len(regions))), "region_count") return info, nil @@ -121,7 +121,10 @@ func (self *SBaiduProvider) GetAccountId() string { } func (self *SBaiduProvider) GetIRegions() ([]cloudprovider.ICloudRegion, error) { - regions := self.client.GetRegions() + regions, err := self.client.GetRegions() + if err != nil { + return nil, err + } ret := []cloudprovider.ICloudRegion{} for i := range regions { ret = append(ret, ®ions[i]) @@ -196,5 +199,5 @@ func (self *SBaiduProvider) GetCloudRegionExternalIdPrefix() string { } func (self *SBaiduProvider) GetMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) { - return nil, cloudprovider.ErrNotImplemented + return self.client.GetMetrics(opts) } diff --git a/pkg/multicloud/baidu/region.go b/pkg/multicloud/baidu/region.go index d3ab07695..cbe6bb0b0 100644 --- a/pkg/multicloud/baidu/region.go +++ b/pkg/multicloud/baidu/region.go @@ -16,14 +16,18 @@ package baidu import ( "fmt" + "net/url" api "yunion.io/x/cloudmux/pkg/apis/compute" "yunion.io/x/cloudmux/pkg/cloudprovider" "yunion.io/x/cloudmux/pkg/multicloud" + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" ) var regions = map[string]string{ "bj": "北京", + "cd": "成都", "gz": "广州", "su": "苏州", "hkg": "香港", @@ -39,27 +43,28 @@ type SRegion struct { multicloud.SNoLbRegion client *SBaiduClient - Region string - RegionName string + RegionId string + RegionName string + RegionEndpoint string } -func (self *SRegion) GetId() string { - return self.Region +func (region *SRegion) GetId() string { + return region.RegionId } -func (self *SRegion) GetGlobalId() string { - return fmt.Sprintf("%s/%s", api.CLOUD_PROVIDER_BAIDU, self.Region) +func (region *SRegion) GetGlobalId() string { + return fmt.Sprintf("%s/%s", api.CLOUD_PROVIDER_BAIDU, region.RegionId) } -func (self *SRegion) GetProvider() string { +func (region *SRegion) GetProvider() string { return api.CLOUD_PROVIDER_BAIDU } -func (self *SRegion) GetCloudEnv() string { +func (region *SRegion) GetCloudEnv() string { return api.CLOUD_PROVIDER_BAIDU } -func (self *SRegion) GetGeographicInfo() cloudprovider.SGeographicInfo { +func (region *SRegion) GetGeographicInfo() cloudprovider.SGeographicInfo { geo, ok := map[string]cloudprovider.SGeographicInfo{ "bj": api.RegionBeijing, "gz": api.RegionGuangzhou, @@ -69,71 +74,273 @@ func (self *SRegion) GetGeographicInfo() cloudprovider.SGeographicInfo { "bd": api.RegionBaoDing, "sin": api.RegionSingapore, "fsh": api.RegionShanghai, - }[self.Region] + "cd": api.RegionChengdu, + }[region.RegionId] if ok { return geo } return cloudprovider.SGeographicInfo{} } -func (self *SRegion) GetName() string { - return self.RegionName +func (region *SRegion) GetName() string { + if name, ok := regions[region.RegionId]; ok { + return name + } + return region.RegionName } -func (self *SRegion) GetI18n() cloudprovider.SModelI18nTable { +func (region *SRegion) GetI18n() cloudprovider.SModelI18nTable { table := cloudprovider.SModelI18nTable{} - table["name"] = cloudprovider.NewSModelI18nEntry(self.GetName()).CN(self.GetName()).EN(self.Region) + table["name"] = cloudprovider.NewSModelI18nEntry(region.GetName()).CN(region.GetName()).EN(region.RegionName) return table } -func (self *SRegion) GetStatus() string { +func (region *SRegion) GetStatus() string { return api.CLOUD_REGION_STATUS_INSERVER } -func (self *SRegion) GetClient() *SBaiduClient { - return self.client +func (region *SRegion) GetClient() *SBaiduClient { + return region.client } -func (self *SRegion) CreateEIP(opts *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) { - return nil, cloudprovider.ErrNotImplemented +func (region *SRegion) CreateEIP(opts *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) { + eip, err := region.CreateEip(opts) + if err != nil { + return nil, err + } + return eip, nil } -func (region *SRegion) CreateISecurityGroup(conf *cloudprovider.SecurityGroupCreateInput) (cloudprovider.ICloudSecurityGroup, error) { - return nil, cloudprovider.ErrNotImplemented +func (region *SRegion) CreateISecurityGroup(opts *cloudprovider.SecurityGroupCreateInput) (cloudprovider.ICloudSecurityGroup, error) { + group, err := region.CreateSecurityGroup(opts) + if err != nil { + return nil, err + } + return group, nil } -func (region *SRegion) GetISecurityGroupById(secgroupId string) (cloudprovider.ICloudSecurityGroup, error) { - return nil, cloudprovider.ErrNotImplemented +func (region *SRegion) GetISecurityGroupById(id string) (cloudprovider.ICloudSecurityGroup, error) { + group, err := region.GetSecurityGroup(id) + if err != nil { + return nil, err + } + return group, nil } -func (self *SRegion) CreateIVpc(opts *cloudprovider.VpcCreateOptions) (cloudprovider.ICloudVpc, error) { - return nil, cloudprovider.ErrNotImplemented +func (region *SRegion) CreateIVpc(opts *cloudprovider.VpcCreateOptions) (cloudprovider.ICloudVpc, error) { + vpc, err := region.CreateVpc(opts) + if err != nil { + return nil, err + } + return vpc, nil } -func (self *SRegion) GetIVpcs() ([]cloudprovider.ICloudVpc, error) { - return nil, cloudprovider.ErrNotImplemented +func (region *SRegion) GetIVpcs() ([]cloudprovider.ICloudVpc, error) { + vpcs, err := region.GetVpcs() + if err != nil { + return nil, errors.Wrapf(err, "GetVpcs") + } + ret := []cloudprovider.ICloudVpc{} + for i := range vpcs { + vpcs[i].region = region + ret = append(ret, &vpcs[i]) + } + return ret, nil } -func (self *SRegion) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) { - return nil, cloudprovider.ErrNotImplemented +func (region *SRegion) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) { + vpc, err := region.GetVpc(id) + if err != nil { + return nil, err + } + return vpc, nil } func (region *SRegion) GetCapabilities() []string { return region.client.GetCapabilities() } -func (self *SRegion) GetIEipById(eipId string) (cloudprovider.ICloudEIP, error) { - return nil, cloudprovider.ErrNotImplemented +func (region *SRegion) GetIEipById(id string) (cloudprovider.ICloudEIP, error) { + eip, err := region.GetEip(id) + if err != nil { + return nil, err + } + return eip, nil +} + +func (region *SRegion) GetIEips() ([]cloudprovider.ICloudEIP, error) { + eips, err := region.GetEips("") + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudEIP{} + for i := range eips { + eips[i].region = region + ret = append(ret, &eips[i]) + } + return ret, nil +} + +func (region *SRegion) GetIZones() ([]cloudprovider.ICloudZone, error) { + zones, err := region.GetZones() + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudZone{} + for i := range zones { + ret = append(ret, &zones[i]) + } + return ret, nil +} + +func (region *SRegion) GetIZoneById(id string) (cloudprovider.ICloudZone, error) { + zones, err := region.GetIZones() + if err != nil { + return nil, err + } + for i := range zones { + if zones[i].GetId() == id || zones[i].GetGlobalId() == id { + return zones[i], nil + } + } + return nil, errors.Wrapf(cloudprovider.ErrNotFound, id) +} + +func (region *SRegion) getStoragecache() *SStoragecache { + return &SStoragecache{region: region} +} + +func (region *SRegion) GetIStoragecaches() ([]cloudprovider.ICloudStoragecache, error) { + return []cloudprovider.ICloudStoragecache{region.getStoragecache()}, nil +} + +func (region *SRegion) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { + caches, err := region.GetIStoragecaches() + if err != nil { + return nil, err + } + for i := range caches { + if caches[i].GetGlobalId() == id { + return caches[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + +func (region *SRegion) GetIHostById(id string) (cloudprovider.ICloudHost, error) { + zones, err := region.GetIZones() + if err != nil { + return nil, err + } + for i := 0; i < len(zones); i += 1 { + ihost, err := zones[i].GetIHostById(id) + if err == nil { + return ihost, nil + } else if errors.Cause(err) != cloudprovider.ErrNotFound { + return nil, err + } + } + return nil, cloudprovider.ErrNotFound +} + +func (region *SRegion) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { + zones, err := region.GetIZones() + if err != nil { + return nil, err + } + for i := 0; i < len(zones); i += 1 { + istore, err := zones[i].GetIStorageById(id) + if err == nil { + return istore, nil + } else if errors.Cause(err) != cloudprovider.ErrNotFound { + return nil, err + } + } + return nil, cloudprovider.ErrNotFound +} + +func (region *SRegion) GetIHosts() ([]cloudprovider.ICloudHost, error) { + ret := make([]cloudprovider.ICloudHost, 0) + + zones, err := region.GetIZones() + if err != nil { + return nil, err + } + for i := 0; i < len(zones); i += 1 { + iZoneHost, err := zones[i].GetIHosts() + if err != nil { + return nil, err + } + ret = append(ret, iZoneHost...) + } + return ret, nil +} + +func (region *SRegion) GetIStorages() ([]cloudprovider.ICloudStorage, error) { + ret := make([]cloudprovider.ICloudStorage, 0) + + zones, err := region.GetIZones() + if err != nil { + return nil, err + } + for i := 0; i < len(zones); i += 1 { + iZoneStores, err := zones[i].GetIStorages() + if err != nil { + return nil, err + } + ret = append(ret, iZoneStores...) + } + return ret, nil +} + +func (region *SRegion) GetIVMs() ([]cloudprovider.ICloudVM, error) { + vms, err := region.GetInstances("", nil) + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudVM{} + for i := range vms { + ret = append(ret, &vms[i]) + } + return ret, nil +} + +func (region *SRegion) GetIVMById(id string) (cloudprovider.ICloudVM, error) { + return region.GetInstance(id) +} + +func (region *SRegion) GetIDiskById(id string) (cloudprovider.ICloudDisk, error) { + return region.GetDisk(id) +} + +func (region *SRegion) bccList(resource string, params url.Values) (jsonutils.JSONObject, error) { + return region.client.bccList(region.RegionId, resource, params) +} + +func (region *SRegion) bccPost(resource string, params url.Values, body map[string]interface{}) (jsonutils.JSONObject, error) { + return region.client.bccPost(region.RegionId, resource, params, body) +} + +func (region *SRegion) bccDelete(resource string, params url.Values) (jsonutils.JSONObject, error) { + return region.client.bccDelete(region.RegionId, resource, params) +} + +func (region *SRegion) bccUpdate(resource string, params url.Values, body map[string]interface{}) (jsonutils.JSONObject, error) { + return region.client.bccUpdate(region.RegionId, resource, params, body) +} + +func (region *SRegion) eipList(resource string, params url.Values) (jsonutils.JSONObject, error) { + return region.client.eipList(region.RegionId, resource, params) } -func (self *SRegion) GetIEips() ([]cloudprovider.ICloudEIP, error) { - return nil, cloudprovider.ErrNotImplemented +func (region *SRegion) eipPost(resource string, params url.Values, body map[string]interface{}) (jsonutils.JSONObject, error) { + return region.client.eipPost(region.RegionId, resource, params, body) } -func (self *SRegion) GetIZones() ([]cloudprovider.ICloudZone, error) { - return nil, cloudprovider.ErrNotImplemented +func (region *SRegion) eipDelete(resource string, params url.Values) (jsonutils.JSONObject, error) { + return region.client.eipDelete(region.RegionId, resource, params) } -func (self *SRegion) GetIZoneById(id string) (cloudprovider.ICloudZone, error) { - return nil, cloudprovider.ErrNotImplemented +func (region *SRegion) eipUpdate(resource string, params url.Values, body map[string]interface{}) (jsonutils.JSONObject, error) { + return region.client.eipUpdate(region.RegionId, resource, params, body) } diff --git a/pkg/multicloud/baidu/secgroup.go b/pkg/multicloud/baidu/secgroup.go new file mode 100644 index 000000000..954a5b02e --- /dev/null +++ b/pkg/multicloud/baidu/secgroup.go @@ -0,0 +1,214 @@ +// 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 baidu + +import ( + "fmt" + "net/url" + "time" + + api "yunion.io/x/cloudmux/pkg/apis/compute" + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/cloudmux/pkg/multicloud" + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/util/secrules" + "yunion.io/x/pkg/utils" +) + +type SSecurityGroup struct { + multicloud.SSecurityGroup + SBaiduTag + region *SRegion + + Id string + Name string + VpcId string + Desc string + CreatedTime time.Time + UpdatedTime time.Time + SgVersion string + Rules []SSecurityGroupRule +} + +func (self *SSecurityGroup) GetVpcId() string { + return self.VpcId +} + +func (self *SSecurityGroup) GetId() string { + return self.Id +} + +func (self *SSecurityGroup) GetGlobalId() string { + return self.Id +} + +func (self *SSecurityGroup) GetDescription() string { + return self.Desc +} + +func (self *SSecurityGroup) GetName() string { + if len(self.Name) > 0 { + return self.Name + } + return self.Name +} + +func (self *SSecurityGroup) GetStatus() string { + return api.SECGROUP_STATUS_READY +} + +func (self *SSecurityGroup) Refresh() error { + group, err := self.region.GetSecurityGroup(self.Id) + if err != nil { + return err + } + return jsonutils.Update(self, group) +} + +func (self *SSecurityGroup) Delete() error { + return self.region.DeleteSecurityGroup(self.Id) +} + +func (self *SSecurityGroup) GetRules() ([]cloudprovider.ISecurityGroupRule, error) { + ret := make([]cloudprovider.ISecurityGroupRule, 0) + for i := range self.Rules { + self.Rules[i].region = self.region + ret = append(ret, &self.Rules[i]) + } + return ret, nil +} + +func (self *SSecurityGroup) CreateRule(opts *cloudprovider.SecurityGroupRuleCreateOptions) (cloudprovider.ISecurityGroupRule, error) { + ruleIds := []string{} + for _, rule := range self.Rules { + ruleIds = append(ruleIds, rule.GetGlobalId()) + } + err := self.region.CreateSecurityGroupRule(self.Id, opts) + if err != nil { + return nil, err + } + err = self.Refresh() + if err != nil { + return nil, err + } + for i := range self.Rules { + if !utils.IsInStringArray(self.Rules[i].GetGlobalId(), ruleIds) { + self.Rules[i].region = self.region + return &self.Rules[i], nil + } + } + return nil, errors.Wrapf(cloudprovider.ErrNotFound, "After created") +} + +func (region *SRegion) CreateSecurityGroupRule(groupId string, opts *cloudprovider.SecurityGroupRuleCreateOptions) error { + params := url.Values{} + params.Set("clientToken", utils.GenRequestId(20)) + params.Set("authorizeRule", "") + rule := map[string]interface{}{ + "remark": opts.Desc, + "protocol": opts.Protocol, + "portRange": opts.Ports, + } + switch opts.Direction { + case secrules.DIR_OUT: + rule["direction"] = "egress" + rule["destIp"] = opts.CIDR + case secrules.DIR_IN: + rule["direction"] = "ingress" + rule["sourceIp"] = opts.CIDR + } + if opts.Protocol == secrules.PROTO_ANY { + rule["protocol"] = "all" + } + body := map[string]interface{}{ + "rule": rule, + } + _, err := region.bccUpdate(fmt.Sprintf("v2/securityGroup/%s", groupId), params, body) + return err +} + +func (region *SRegion) GetSecurityGroups(vpcId string) ([]SSecurityGroup, error) { + params := url.Values{} + if len(vpcId) > 0 { + params.Set("vpcId", vpcId) + } + ret := []SSecurityGroup{} + for { + resp, err := region.bccList("v2/securityGroup", params) + if err != nil { + return nil, err + } + part := struct { + NextMarker string + SecurityGroups []SSecurityGroup + }{} + err = resp.Unmarshal(&part) + if err != nil { + return nil, err + } + ret = append(ret, part.SecurityGroups...) + if len(part.NextMarker) == 0 { + break + } + params.Set("marker", part.NextMarker) + } + return ret, nil +} + +func (region *SRegion) DeleteSecurityGroup(id string) error { + _, err := region.bccDelete(fmt.Sprintf("v2/securityGroup/%s", id), nil) + return err +} + +func (region *SRegion) GetSecurityGroup(id string) (*SSecurityGroup, error) { + resp, err := region.bccList(fmt.Sprintf("v2/securityGroup/%s", id), nil) + if err != nil { + return nil, err + } + ret := &SSecurityGroup{region: region} + err = resp.Unmarshal(ret) + if err != nil { + return nil, err + } + return ret, nil +} + +func (region *SRegion) CreateSecurityGroup(opts *cloudprovider.SecurityGroupCreateInput) (*SSecurityGroup, error) { + params := url.Values{} + params.Set("clientToken", utils.GenRequestId(20)) + tags := []BaiduTag{} + for k, v := range opts.Tags { + tags = append(tags, BaiduTag{ + TagKey: k, + TagValue: v, + }) + } + body := map[string]interface{}{ + "name": opts.Name, + "vpcId": opts.VpcId, + "desc": opts.Desc, + "tags": tags, + } + resp, err := region.bccPost("v2/securityGroup", params, body) + if err != nil { + return nil, err + } + groupId, err := resp.GetString("securityGroupId") + if err != nil { + return nil, err + } + return region.GetSecurityGroup(groupId) +} diff --git a/pkg/multicloud/baidu/secgrouprule.go b/pkg/multicloud/baidu/secgrouprule.go new file mode 100644 index 000000000..262ce339e --- /dev/null +++ b/pkg/multicloud/baidu/secgrouprule.go @@ -0,0 +1,146 @@ +// 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 baidu + +import ( + "fmt" + "net/url" + "strings" + "time" + + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/pkg/util/secrules" +) + +type SSecurityGroupRule struct { + region *SRegion + + Remark string + Direction string + Ethertype string + PortRange string + DestGroupId string + DestIp string + SourceIp string + SourceGroupId string + SecurityGroupId string + SecurityGroupRuleId string + CreatedTime time.Time + UpdatedTime time.Time + Protocol string +} + +func (self *SSecurityGroupRule) GetGlobalId() string { + return self.SecurityGroupRuleId +} + +func (self *SSecurityGroupRule) GetAction() secrules.TSecurityRuleAction { + return secrules.SecurityRuleAllow +} + +func (self *SSecurityGroupRule) GetDescription() string { + return self.Remark +} + +func (self *SSecurityGroupRule) GetDirection() secrules.TSecurityRuleDirection { + if self.Direction == "ingress" { + return secrules.DIR_IN + } + return secrules.DIR_OUT +} + +func getCidr(ip string, version string) string { + switch version { + case "IPv6": + if ip == "all" { + return "::/0" + } + return ip + case "IPv4": + if ip == "all" { + return "0.0.0.0/0" + } + return ip + default: + return ip + } +} + +func (self *SSecurityGroupRule) GetCIDRs() []string { + ret := []string{} + if len(self.DestGroupId) > 0 { + ret = append(ret, self.DestGroupId) + } + if len(self.DestIp) > 0 { + ret = append(ret, getCidr(self.DestIp, self.Ethertype)) + } + if len(self.SourceIp) > 0 { + ret = append(ret, getCidr(self.SourceIp, self.Ethertype)) + } + if len(self.SourceGroupId) > 0 { + ret = append(ret, self.SourceGroupId) + } + return ret +} + +func (self *SSecurityGroupRule) GetProtocol() string { + if strings.ToLower(self.Protocol) == "all" || len(self.Protocol) == 0 { + return secrules.PROTO_ANY + } + return strings.ToLower(self.Protocol) +} + +func (self *SSecurityGroupRule) GetPorts() string { + if self.PortRange == "1-65535" || self.PortRange == "" { + return "" + } + return self.PortRange +} + +func (self *SSecurityGroupRule) GetPriority() int { + return 1 +} + +func (self *SSecurityGroupRule) Delete() error { + return self.region.DeleteSecurityGroupRule(self.SecurityGroupRuleId) +} + +func (region *SRegion) DeleteSecurityGroupRule(id string) error { + _, err := region.bccDelete(fmt.Sprintf("v2/securityGroup/rule/%s", id), nil) + return err +} + +func (self *SSecurityGroupRule) Update(opts *cloudprovider.SecurityGroupRuleUpdateOptions) error { + return self.region.UpdateSecurityGroupRule(self.GetGlobalId(), self.Direction, opts) +} + +func (region *SRegion) UpdateSecurityGroupRule(ruleId string, direction string, opts *cloudprovider.SecurityGroupRuleUpdateOptions) error { + params := url.Values{} + body := map[string]interface{}{ + "remark": opts.Desc, + "protocol": opts.Protocol, + "portRange": opts.Ports, + "securityGroupRuleId": ruleId, + } + if len(opts.CIDR) > 0 { + if direction == "egress" { + body["destIp"] = opts.CIDR + } else { + body["sourceIp"] = opts.CIDR + } + } + _, err := region.bccUpdate("v2/securityGroup/rule/update", params, body) + return err +} diff --git a/pkg/multicloud/baidu/shell/disk.go b/pkg/multicloud/baidu/shell/disk.go new file mode 100644 index 000000000..11f6b676f --- /dev/null +++ b/pkg/multicloud/baidu/shell/disk.go @@ -0,0 +1,49 @@ +// 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/pkg/errors" + "yunion.io/x/pkg/util/shellutils" + + "yunion.io/x/cloudmux/pkg/multicloud/baidu" +) + +func init() { + type diskListOptions struct { + ZoneName string + StorageType string + InstanceId string + } + shellutils.R(&diskListOptions{}, "disk-list", "list disks", func(cli *baidu.SRegion, args *diskListOptions) error { + disks, err := cli.GetDisks(args.StorageType, args.ZoneName, args.InstanceId) + if err != nil { + return err + } + printList(disks) + return nil + }) + type diskShowOptions struct { + ID string `help:"ID of disk to show"` + } + shellutils.R(&diskShowOptions{}, "disk-show", "list disks", func(cli *baidu.SRegion, args *diskShowOptions) error { + disk, err := cli.GetDisk(args.ID) + if err != nil { + return errors.Wrap(err, "Getdisk") + } + printObject(disk) + return nil + }) +} diff --git a/pkg/multicloud/baidu/shell/eip.go b/pkg/multicloud/baidu/shell/eip.go new file mode 100644 index 000000000..771ed93cc --- /dev/null +++ b/pkg/multicloud/baidu/shell/eip.go @@ -0,0 +1,62 @@ +// 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/pkg/errors" + "yunion.io/x/pkg/util/shellutils" + + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/cloudmux/pkg/multicloud/baidu" +) + +func init() { + type EipListOptions struct { + InstanceId string + } + shellutils.R(&EipListOptions{}, "eip-list", "list eips", func(cli *baidu.SRegion, args *EipListOptions) error { + eips, err := cli.GetEips(args.InstanceId) + if err != nil { + return err + } + printList(eips) + return nil + }) + type EipIdOptions struct { + ID string `help:"ID of eip to show"` + } + shellutils.R(&EipIdOptions{}, "eip-show", "list eips", func(cli *baidu.SRegion, args *EipIdOptions) error { + eip, err := cli.GetEip(args.ID) + if err != nil { + return errors.Wrap(err, "GetEip") + } + printObject(eip) + return nil + }) + + shellutils.R(&EipIdOptions{}, "eip-delete", "delete eip", func(cli *baidu.SRegion, args *EipIdOptions) error { + return cli.DeleteEip(args.ID) + }) + + shellutils.R(&cloudprovider.SEip{}, "eip-create", "create eip", func(cli *baidu.SRegion, args *cloudprovider.SEip) error { + eip, err := cli.CreateEip(args) + if err != nil { + return err + } + printObject(eip) + return nil + }) + +} diff --git a/pkg/multicloud/baidu/shell/image.go b/pkg/multicloud/baidu/shell/image.go new file mode 100644 index 000000000..0e4f688db --- /dev/null +++ b/pkg/multicloud/baidu/shell/image.go @@ -0,0 +1,53 @@ +// 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/pkg/util/shellutils" + + "yunion.io/x/cloudmux/pkg/multicloud/baidu" +) + +func init() { + type ImageListOptions struct { + ImageType string + } + shellutils.R(&ImageListOptions{}, "image-list", "list images", func(cli *baidu.SRegion, args *ImageListOptions) error { + images, err := cli.GetImages(args.ImageType) + if err != nil { + return err + } + printList(images) + return nil + }) + + type ImageIdOptions struct { + ID string + } + + shellutils.R(&ImageIdOptions{}, "image-show", "show image", func(cli *baidu.SRegion, args *ImageIdOptions) error { + image, err := cli.GetImage(args.ID) + if err != nil { + return err + } + printObject(image) + return nil + }) + + shellutils.R(&ImageIdOptions{}, "image-delete", "delete image", func(cli *baidu.SRegion, args *ImageIdOptions) error { + return cli.DeleteImage(args.ID) + }) + +} diff --git a/pkg/multicloud/baidu/shell/instances.go b/pkg/multicloud/baidu/shell/instances.go new file mode 100644 index 000000000..d1db8e6d5 --- /dev/null +++ b/pkg/multicloud/baidu/shell/instances.go @@ -0,0 +1,50 @@ +// 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/cloudmux/pkg/multicloud/baidu" + + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/util/shellutils" +) + +func init() { + type InstanceListOptions struct { + Id []string `help:"IDs of instances to show"` + ZoneName string + } + shellutils.R(&InstanceListOptions{}, "instance-list", "list instances", func(cli *baidu.SRegion, args *InstanceListOptions) error { + res, err := cli.GetInstances(args.ZoneName, args.Id) + if err != nil { + return errors.Wrap(err, "GetInstances") + } + printList(res) + return nil + }) + + type InstanceShowOptions struct { + ID string `help:"ID of instance to show"` + } + shellutils.R(&InstanceShowOptions{}, "instance-show", "show instance", func(cli *baidu.SRegion, args *InstanceShowOptions) error { + res, err := cli.GetInstance(args.ID) + if err != nil { + return errors.Wrap(err, "GetInstance") + } + printObject(res) + return nil + }) + +} diff --git a/pkg/multicloud/baidu/shell/keypair.go b/pkg/multicloud/baidu/shell/keypair.go new file mode 100644 index 000000000..b6d8eb599 --- /dev/null +++ b/pkg/multicloud/baidu/shell/keypair.go @@ -0,0 +1,35 @@ +// 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/pkg/util/shellutils" + + "yunion.io/x/cloudmux/pkg/multicloud/baidu" +) + +func init() { + type KeypairListOptions struct { + } + shellutils.R(&KeypairListOptions{}, "keypair-list", "list keypairs", func(cli *baidu.SRegion, args *KeypairListOptions) error { + keypairs, err := cli.GetKeypairs() + if err != nil { + return err + } + printList(keypairs) + return nil + }) + +} diff --git a/pkg/multicloud/baidu/shell/monitor.go b/pkg/multicloud/baidu/shell/monitor.go new file mode 100644 index 000000000..f8349a377 --- /dev/null +++ b/pkg/multicloud/baidu/shell/monitor.go @@ -0,0 +1,37 @@ +// 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/log" + "yunion.io/x/pkg/util/shellutils" + + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/cloudmux/pkg/multicloud/baidu" +) + +func init() { + shellutils.R(&cloudprovider.MetricListOptions{}, "metric-list", "List metrics", func(cli *baidu.SRegion, args *cloudprovider.MetricListOptions) error { + metrics, err := cli.GetClient().GetMetrics(args) + if err != nil { + return err + } + for i := range metrics { + log.Infof("metric: %s %s %s", metrics[i].Id, metrics[i].MetricType, metrics[i].Unit) + printList(metrics[i].Values) + } + return nil + }) +} diff --git a/pkg/multicloud/baidu/shell/network.go b/pkg/multicloud/baidu/shell/network.go new file mode 100644 index 000000000..0915c9de6 --- /dev/null +++ b/pkg/multicloud/baidu/shell/network.go @@ -0,0 +1,69 @@ +// 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/pkg/util/shellutils" + + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/cloudmux/pkg/multicloud/baidu" +) + +func init() { + type NetworkListOptions struct { + VpcId string + ZoneName string + } + shellutils.R(&NetworkListOptions{}, "network-list", "list networks", func(cli *baidu.SRegion, args *NetworkListOptions) error { + networks, err := cli.GetNetworks(args.VpcId, args.ZoneName) + if err != nil { + return err + } + printList(networks) + return nil + }) + + type NetworkIdOptions struct { + ID string + } + shellutils.R(&NetworkIdOptions{}, "network-show", "show network", func(cli *baidu.SRegion, args *NetworkIdOptions) error { + network, err := cli.GetNetwork(args.ID) + if err != nil { + return err + } + printObject(network) + return nil + }) + + shellutils.R(&NetworkIdOptions{}, "network-delete", "delete network", func(cli *baidu.SRegion, args *NetworkIdOptions) error { + return cli.DeleteNetwork(args.ID) + }) + + type NetworkCreateOptions struct { + ZONE string + VPC string + cloudprovider.SNetworkCreateOptions + } + + shellutils.R(&NetworkCreateOptions{}, "network-create", "create network", func(cli *baidu.SRegion, args *NetworkCreateOptions) error { + network, err := cli.CreateNetwork(args.ZONE, args.VPC, &args.SNetworkCreateOptions) + if err != nil { + return err + } + printObject(network) + return nil + }) + +} diff --git a/pkg/multicloud/baidu/shell/printutil.go b/pkg/multicloud/baidu/shell/printutil.go index 525587e9d..36d30c120 100644 --- a/pkg/multicloud/baidu/shell/printutil.go +++ b/pkg/multicloud/baidu/shell/printutil.go @@ -18,8 +18,8 @@ import ( "yunion.io/x/pkg/util/printutils" ) -func printList(data interface{}, total, offset, limit int, columns []string) { - printutils.PrintInterfaceList(data, total, offset, limit, columns) +func printList(data interface{}) { + printutils.PrintInterfaceList(data, 0, 0, 0, nil) } func printObject(obj interface{}) { diff --git a/pkg/multicloud/baidu/shell/region.go b/pkg/multicloud/baidu/shell/region.go index 3387e8d25..df2393720 100644 --- a/pkg/multicloud/baidu/shell/region.go +++ b/pkg/multicloud/baidu/shell/region.go @@ -24,8 +24,11 @@ func init() { type RegionListOptions struct { } shellutils.R(&RegionListOptions{}, "region-list", "list regions", func(cli *baidu.SRegion, args *RegionListOptions) error { - regions := cli.GetClient().GetRegions() - printList(regions, 0, 0, 0, []string{}) + regions, err := cli.GetClient().GetRegions() + if err != nil { + return err + } + printList(regions) return nil }) diff --git a/pkg/multicloud/baidu/shell/secgroup.go b/pkg/multicloud/baidu/shell/secgroup.go new file mode 100644 index 000000000..189a0441b --- /dev/null +++ b/pkg/multicloud/baidu/shell/secgroup.go @@ -0,0 +1,63 @@ +// 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/pkg/util/shellutils" + + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/cloudmux/pkg/multicloud/baidu" +) + +func init() { + type SecurityGroupListOptions struct { + VpcId string + } + shellutils.R(&SecurityGroupListOptions{}, "security-group-list", "list security groups", func(cli *baidu.SRegion, args *SecurityGroupListOptions) error { + secgroups, err := cli.GetSecurityGroups(args.VpcId) + if err != nil { + return err + } + printList(secgroups) + return nil + }) + + type SecurityGroupIdOptions struct { + ID string + } + + shellutils.R(&SecurityGroupIdOptions{}, "security-group-show", "show security group", func(cli *baidu.SRegion, args *SecurityGroupIdOptions) error { + secgroup, err := cli.GetSecurityGroup(args.ID) + if err != nil { + return err + } + printObject(secgroup) + return nil + }) + + shellutils.R(&SecurityGroupIdOptions{}, "security-group-delete", "delete security group", func(cli *baidu.SRegion, args *SecurityGroupIdOptions) error { + return cli.DeleteSecurityGroup(args.ID) + }) + + shellutils.R(&cloudprovider.SecurityGroupCreateInput{}, "security-group-create", "create security group", func(cli *baidu.SRegion, args *cloudprovider.SecurityGroupCreateInput) error { + group, err := cli.CreateSecurityGroup(args) + if err != nil { + return err + } + printObject(group) + return nil + }) + +} diff --git a/pkg/multicloud/baidu/shell/snapshot.go b/pkg/multicloud/baidu/shell/snapshot.go new file mode 100644 index 000000000..68fad13c2 --- /dev/null +++ b/pkg/multicloud/baidu/shell/snapshot.go @@ -0,0 +1,67 @@ +// 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/pkg/errors" + "yunion.io/x/pkg/util/shellutils" + + "yunion.io/x/cloudmux/pkg/multicloud/baidu" +) + +func init() { + type SnapshotListOptions struct { + DiskId string + } + shellutils.R(&SnapshotListOptions{}, "snapshot-list", "list snapshots", func(cli *baidu.SRegion, args *SnapshotListOptions) error { + snapshots, err := cli.GetSnapshots(args.DiskId) + if err != nil { + return err + } + printList(snapshots) + return nil + }) + type SnapshotIdOptions struct { + ID string `help:"ID of snapshot to show"` + } + shellutils.R(&SnapshotIdOptions{}, "snapshot-show", "list snapshots", func(cli *baidu.SRegion, args *SnapshotIdOptions) error { + snapshot, err := cli.GetSnapshot(args.ID) + if err != nil { + return errors.Wrap(err, "GetSnapshot") + } + printObject(snapshot) + return nil + }) + + shellutils.R(&SnapshotIdOptions{}, "snapshot-delete", "delete snapshot", func(cli *baidu.SRegion, args *SnapshotIdOptions) error { + return cli.DeleteSnapshot(args.ID) + }) + + type SnapshotCreateOptions struct { + NAME string + Desc string + DISK string + } + + shellutils.R(&SnapshotCreateOptions{}, "snapshot-create", "create snapshot", func(cli *baidu.SRegion, args *SnapshotCreateOptions) error { + snapshot, err := cli.CreateSnapshot(args.NAME, args.Desc, args.DISK) + if err != nil { + return err + } + printObject(snapshot) + return nil + }) + +} diff --git a/pkg/multicloud/baidu/shell/storage.go b/pkg/multicloud/baidu/shell/storage.go new file mode 100644 index 000000000..137b4b1ef --- /dev/null +++ b/pkg/multicloud/baidu/shell/storage.go @@ -0,0 +1,36 @@ +// 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/pkg/util/shellutils" + + "yunion.io/x/cloudmux/pkg/multicloud/baidu" +) + +func init() { + type StorageListOptions struct { + ZoneName string + } + shellutils.R(&StorageListOptions{}, "storage-list", "list storage", func(cli *baidu.SRegion, args *StorageListOptions) error { + storages, err := cli.GetStorageTypes(args.ZoneName) + if err != nil { + return err + } + printList(storages) + return nil + }) + +} diff --git a/pkg/multicloud/baidu/shell/sts.go b/pkg/multicloud/baidu/shell/sts.go new file mode 100644 index 000000000..714a344ce --- /dev/null +++ b/pkg/multicloud/baidu/shell/sts.go @@ -0,0 +1,34 @@ +// 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/pkg/util/shellutils" + + "yunion.io/x/cloudmux/pkg/multicloud/baidu" +) + +func init() { + type StsListOptions struct { + } + shellutils.R(&StsListOptions{}, "caller-show", "show caller", func(cli *baidu.SRegion, args *StsListOptions) error { + ret, err := cli.GetClient().GetSessionToken() + if err != nil { + return err + } + printObject(ret) + return nil + }) +} diff --git a/pkg/multicloud/baidu/shell/vpc.go b/pkg/multicloud/baidu/shell/vpc.go new file mode 100644 index 000000000..88f043a91 --- /dev/null +++ b/pkg/multicloud/baidu/shell/vpc.go @@ -0,0 +1,61 @@ +// 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/pkg/errors" + "yunion.io/x/pkg/util/shellutils" + + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/cloudmux/pkg/multicloud/baidu" +) + +func init() { + type VpcListOptions struct { + } + shellutils.R(&VpcListOptions{}, "vpc-list", "list vpcs", func(cli *baidu.SRegion, args *VpcListOptions) error { + vpcs, err := cli.GetVpcs() + if err != nil { + return err + } + printList(vpcs) + return nil + }) + type VpcIdOptions struct { + ID string `help:"ID of vpc to show"` + } + shellutils.R(&VpcIdOptions{}, "vpc-show", "list vpcs", func(cli *baidu.SRegion, args *VpcIdOptions) error { + vpc, err := cli.GetVpc(args.ID) + if err != nil { + return errors.Wrap(err, "GetVpc") + } + printObject(vpc) + return nil + }) + + shellutils.R(&VpcIdOptions{}, "vpc-delete", "delete vpc", func(cli *baidu.SRegion, args *VpcIdOptions) error { + return cli.DeleteVpc(args.ID) + }) + + shellutils.R(&cloudprovider.VpcCreateOptions{}, "vpc-create", "create vpc", func(cli *baidu.SRegion, args *cloudprovider.VpcCreateOptions) error { + vpc, err := cli.CreateVpc(args) + if err != nil { + return err + } + printObject(vpc) + return nil + }) + +} diff --git a/pkg/multicloud/baidu/shell/zone.go b/pkg/multicloud/baidu/shell/zone.go new file mode 100644 index 000000000..1a5ccf893 --- /dev/null +++ b/pkg/multicloud/baidu/shell/zone.go @@ -0,0 +1,35 @@ +// 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/pkg/util/shellutils" + + "yunion.io/x/cloudmux/pkg/multicloud/baidu" +) + +func init() { + type ZoneListOptions struct { + } + shellutils.R(&ZoneListOptions{}, "zone-list", "list zones", func(cli *baidu.SRegion, args *ZoneListOptions) error { + zones, err := cli.GetZones() + if err != nil { + return err + } + printList(zones) + return nil + }) + +} diff --git a/pkg/multicloud/baidu/signer.go b/pkg/multicloud/baidu/signer.go index 1cb651e7c..c2db16fe2 100644 --- a/pkg/multicloud/baidu/signer.go +++ b/pkg/multicloud/baidu/signer.go @@ -61,7 +61,7 @@ func getCanonicalQueryString(params url.Values) string { } result := make([]string, 0, len(params)) for k, v := range params { - if strings.ToLower(k) == strings.ToLower("authorization") { + if strings.EqualFold(k, "authorization") { continue } item := "" @@ -101,13 +101,13 @@ func getCanonicalHeaders(headers http.Header, return strings.Join(canonicalHeaders, "\n"), signHeaders } -func (self *SBaiduClient) sign(req *http.Request) (string, error) { +func (cli *SBaiduClient) sign(req *http.Request) (string, error) { signKeyInfo := fmt.Sprintf("%s/%s/%s/%d", "bce-auth-v1", - self.accessKeyId, + cli.accessKeyId, time.Now().UTC().Format(ISO8601), 1800) - hasher := hmac.New(sha256.New, []byte(self.accessKeySecret)) + hasher := hmac.New(sha256.New, []byte(cli.accessKeySecret)) hasher.Write([]byte(signKeyInfo)) signKey := hex.EncodeToString(hasher.Sum(nil)) canonicalUri := getCanonicalURIPath(req.URL.Path) diff --git a/pkg/multicloud/baidu/snapshot.go b/pkg/multicloud/baidu/snapshot.go new file mode 100644 index 000000000..91271380f --- /dev/null +++ b/pkg/multicloud/baidu/snapshot.go @@ -0,0 +1,178 @@ +// 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 baidu + +import ( + "fmt" + "net/url" + "strings" + "time" + + api "yunion.io/x/cloudmux/pkg/apis/compute" + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/cloudmux/pkg/multicloud" + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/utils" +) + +type SSnapshot struct { + multicloud.SVirtualResourceBase + region *SRegion + SBaiduTag + + Id string + Desc string + VolumeId string + CreateMethod string + Status string + SizeInGb int32 + Name string + CreateTime time.Time +} + +func (self *SSnapshot) GetId() string { + return self.Id +} + +func (self *SSnapshot) GetName() string { + return self.Name +} + +func (self *SSnapshot) GetStatus() string { + switch self.Status { + case "Creating": + return api.SNAPSHOT_CREATING + case "CreatedFailed": + return api.SNAPSHOT_FAILED + case "Available": + return api.SNAPSHOT_READY + case "NotAvailable": + return api.SNAPSHOT_UNKNOWN + default: + return strings.ToLower(self.Status) + } +} + +func (self *SSnapshot) GetSizeMb() int32 { + return self.SizeInGb * 1024 +} + +func (self *SSnapshot) GetDiskId() string { + return self.VolumeId +} + +func (self *SSnapshot) GetDiskType() string { + return "" +} + +func (self *SSnapshot) Refresh() error { + snapshot, err := self.region.GetSnapshot(self.Id) + if err != nil { + return err + } + return jsonutils.Update(self, snapshot) +} + +func (self *SSnapshot) GetGlobalId() string { + return self.Id +} + +func (self *SRegion) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) { + snapshots, err := self.GetSnapshots("") + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudSnapshot{} + for i := 0; i < len(snapshots); i += 1 { + snapshots[i].region = self + ret = append(ret, &snapshots[i]) + } + return ret, nil +} + +func (self *SRegion) GetISnapshotById(id string) (cloudprovider.ICloudSnapshot, error) { + snapshot, err := self.GetSnapshot(id) + if err != nil { + return nil, err + } + return snapshot, nil +} + +func (self *SSnapshot) Delete() error { + return self.region.DeleteSnapshot(self.Id) +} + +func (region *SRegion) GetSnapshots(diskId string) ([]SSnapshot, error) { + params := url.Values{} + params.Set("volumeId", diskId) + ret := []SSnapshot{} + for { + resp, err := region.bccList("v2/snapshot", params) + if err != nil { + return nil, err + } + part := struct { + NextMarker string + Snapshots []SSnapshot + }{} + err = resp.Unmarshal(&part) + if err != nil { + return nil, err + } + ret = append(ret, part.Snapshots...) + if len(part.NextMarker) == 0 { + break + } + params.Set("marker", part.NextMarker) + } + return ret, nil +} + +func (region *SRegion) GetSnapshot(id string) (*SSnapshot, error) { + resp, err := region.bccList(fmt.Sprintf("v2/snapshot/%s", id), nil) + if err != nil { + return nil, err + } + ret := &SSnapshot{region: region} + err = resp.Unmarshal(ret, "snapshot") + if err != nil { + return nil, err + } + return ret, nil +} + +func (region *SRegion) DeleteSnapshot(id string) error { + _, err := region.bccDelete(fmt.Sprintf("v2/snapshot/%s", id), nil) + return err +} + +func (region *SRegion) CreateSnapshot(name, desc, diskId string) (*SSnapshot, error) { + params := url.Values{} + params.Set("clicentToken", utils.GenRequestId(20)) + body := map[string]interface{}{ + "volumeId": diskId, + "snapshotName": name, + "desc": desc, + } + resp, err := region.bccPost("v2/snapshot", params, body) + if err != nil { + return nil, err + } + snapshotId, err := resp.GetString("snapshotId") + if err != nil { + return nil, err + } + return region.GetSnapshot(snapshotId) +} diff --git a/pkg/multicloud/baidu/snapshotpolicy.go b/pkg/multicloud/baidu/snapshotpolicy.go new file mode 100644 index 000000000..c851725e8 --- /dev/null +++ b/pkg/multicloud/baidu/snapshotpolicy.go @@ -0,0 +1,207 @@ +// 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 baidu + +import ( + "fmt" + "net/url" + "time" + + "yunion.io/x/cloudmux/pkg/apis" + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/cloudmux/pkg/multicloud" + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/utils" +) + +type SSnapshotPolicy struct { + multicloud.SVirtualResourceBase + SBaiduTag + region *SRegion + + Id string + Name string + TimePoints []int + RepeatWeekdays []int + RetentionDays int + Status string + CreatedTime time.Time + UpdatedTime time.Time + DeletedTime time.Time + LastExecuteTime time.Time + VolumeCount int +} + +func (self *SSnapshotPolicy) GetId() string { + return self.Id +} + +func (self *SSnapshotPolicy) GetName() string { + return self.Name +} + +func (self *SSnapshotPolicy) GetStatus() string { + switch self.Status { + case "active": + return apis.STATUS_AVAILABLE + case "deleted": + return apis.STATUS_DELETING + case "paused": + return apis.STATUS_UNKNOWN + default: + return apis.STATUS_UNKNOWN + } +} + +func (self *SSnapshotPolicy) Refresh() error { + policy, err := self.region.GetSnapshotPolicy(self.Id) + if err != nil { + return err + } + return jsonutils.Update(self, policy) +} + +func (self *SSnapshotPolicy) GetGlobalId() string { + return self.Id +} + +func (self *SSnapshotPolicy) GetRetentionDays() int { + return self.RetentionDays +} + +func (self *SSnapshotPolicy) GetRepeatWeekdays() ([]int, error) { + return self.RepeatWeekdays, nil +} + +func (self *SSnapshotPolicy) GetTimePoints() ([]int, error) { + return self.TimePoints, nil +} + +func (self *SSnapshotPolicy) Delete() error { + return self.region.DeletSnapshotPolicy(self.Id) +} + +func (self *SSnapshotPolicy) ApplyDisks(ids []string) error { + return self.region.ApplySnapshotPolicy(self.Id, ids) +} + +func (self *SSnapshotPolicy) CancelDisks(ids []string) error { + return self.region.CancelSnapshotPolicy(self.Id, ids) +} + +func (self *SSnapshotPolicy) GetApplyDiskIds() ([]string, error) { + return nil, cloudprovider.ErrNotSupported +} + +func (region *SRegion) GetSnapshotPolicys() ([]SSnapshotPolicy, error) { + params := url.Values{} + ret := []SSnapshotPolicy{} + for { + resp, err := region.bccList("v2/asp", params) + if err != nil { + return nil, err + } + part := struct { + NextMarker string + AutoSnapshotPolicys []SSnapshotPolicy + }{} + err = resp.Unmarshal(&part) + if err != nil { + return nil, err + } + ret = append(ret, part.AutoSnapshotPolicys...) + if len(part.NextMarker) == 0 { + break + } + params.Set("marker", part.NextMarker) + } + return ret, nil +} + +func (region *SRegion) GetSnapshotPolicy(id string) (*SSnapshotPolicy, error) { + params := url.Values{} + resp, err := region.bccList(fmt.Sprintf("v2/asp/%s", id), params) + if err != nil { + return nil, err + } + ret := &SSnapshotPolicy{region: region} + err = resp.Unmarshal(ret, "autoSnapshotPolicy") + if err != nil { + return nil, err + } + return ret, nil +} + +func (r *SRegion) CreateSnapshotPolicy(opts *cloudprovider.SnapshotPolicyInput) (string, error) { + params := url.Values{} + params.Set("clientToken", utils.GenRequestId(20)) + body := map[string]interface{}{ + "name": opts.Name, + "timePoints": opts.TimePoints, + "repeatWeekdays": opts.RetentionDays, + "retentionDays": opts.RetentionDays, + } + resp, err := r.bccPost("v2/asp", params, body) + if err != nil { + return "", err + } + return resp.GetString("aspId") +} + +func (r *SRegion) GetISnapshotPolicyById(id string) (cloudprovider.ICloudSnapshotPolicy, error) { + policy, err := r.GetSnapshotPolicy(id) + if err != nil { + return nil, err + } + return policy, nil +} + +func (self *SRegion) GetISnapshotPolicies() ([]cloudprovider.ICloudSnapshotPolicy, error) { + policys, err := self.GetSnapshotPolicys() + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudSnapshotPolicy{} + for i := range policys { + policys[i].region = self + ret = append(ret, &policys[i]) + } + return ret, nil +} + +func (self *SRegion) DeletSnapshotPolicy(id string) error { + _, err := self.bccList(fmt.Sprintf("v2/asp/%s", id), nil) + return err +} + +func (self *SRegion) ApplySnapshotPolicy(id string, diskIds []string) error { + params := url.Values{} + params.Set("attach", "") + body := map[string]interface{}{ + "volumeIds": diskIds, + } + _, err := self.bccUpdate(fmt.Sprintf("v2/asp/%s", id), params, body) + return err +} + +func (self *SRegion) CancelSnapshotPolicy(id string, diskIds []string) error { + params := url.Values{} + params.Set("detach", "") + body := map[string]interface{}{ + "volumeIds": diskIds, + } + _, err := self.bccUpdate(fmt.Sprintf("v2/asp/%s", id), params, body) + return err +} diff --git a/pkg/multicloud/baidu/storage.go b/pkg/multicloud/baidu/storage.go new file mode 100644 index 000000000..359267025 --- /dev/null +++ b/pkg/multicloud/baidu/storage.go @@ -0,0 +1,176 @@ +// 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 baidu + +import ( + "fmt" + "net/url" + "strings" + + api "yunion.io/x/cloudmux/pkg/apis/compute" + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/cloudmux/pkg/multicloud" + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/utils" +) + +type SStorage struct { + multicloud.SStorageBase + + zone *SZone + StorageType string + MinDiskSize int + MaxDiskSize int +} + +var storageMap = map[string]string{ + "enhanced_ssd_pl1": "enhanced_ssd_pl1", + "enhanced_ssd_pl2": "enhanced_ssd_pl2", + "enhanced_ssd_pl3": "enhanced_ssd_pl3", + "cloud_hp1": "premium_ssd", + "hp1": "ssd", +} + +func isMatchStorageType(v1, v2 string) bool { + e1, _ := storageMap[v1] + e2, _ := storageMap[v2] + return v1 == v2 || v1 == e2 || e1 == v2 || e1 == e2 +} + +func (storage *SStorage) GetId() string { + return fmt.Sprintf("%s-%s-%s", storage.zone.region.client.cpcfg.Id, storage.zone.GetId(), storage.StorageType) +} + +func (storage *SStorage) GetName() string { + return fmt.Sprintf("%s-%s-%s", storage.zone.region.client.cpcfg.Name, storage.zone.GetId(), storage.StorageType) +} + +func (storage *SStorage) GetGlobalId() string { + return fmt.Sprintf("%s-%s-%s", storage.zone.region.client.cpcfg.Id, storage.zone.GetGlobalId(), storage.StorageType) +} + +func (storage *SStorage) IsEmulated() bool { + return true +} + +func (storage *SStorage) GetIZone() cloudprovider.ICloudZone { + return storage.zone +} + +func (storage *SStorage) GetIDisks() ([]cloudprovider.ICloudDisk, error) { + disks, err := storage.zone.region.GetDisks(storage.StorageType, storage.zone.ZoneName, "") + if err != nil { + return nil, errors.Wrap(err, "region.GetDisks") + } + ret := []cloudprovider.ICloudDisk{} + for i := range disks { + disks[i].storage = storage + ret = append(ret, &disks[i]) + } + return ret, nil +} + +func (storage *SStorage) GetStorageType() string { + return storage.StorageType +} + +func (storage *SStorage) GetMediumType() string { + if strings.Contains(strings.ToLower(storage.StorageType), "ssd") { + return api.DISK_TYPE_SSD + } + return api.DISK_TYPE_ROTATE +} + +func (storage *SStorage) GetCapacityMB() int64 { + return 0 +} + +func (storage *SStorage) GetCapacityUsedMB() int64 { + return 0 +} + +func (storage *SStorage) GetStorageConf() jsonutils.JSONObject { + return jsonutils.NewDict() +} + +func (storage *SStorage) GetEnabled() bool { + return true +} + +func (storage *SStorage) CreateIDisk(opts *cloudprovider.DiskCreateConfig) (cloudprovider.ICloudDisk, error) { + disk, err := storage.zone.region.CreateDisk(storage.StorageType, storage.zone.ZoneName, opts) + if err != nil { + return nil, err + } + disk.storage = storage + return disk, nil +} + +func (storage *SStorage) GetIDiskById(id string) (cloudprovider.ICloudDisk, error) { + disk, err := storage.zone.region.GetDisk(id) + if err != nil { + return nil, err + } + disk.storage = storage + return disk, nil +} + +func (storage *SStorage) GetMountPoint() string { + return "" +} + +func (storage *SStorage) IsSysDiskStore() bool { + return true +} + +func (storage *SStorage) DisableSync() bool { + return false +} + +func (storage *SStorage) GetIStoragecache() cloudprovider.ICloudStoragecache { + return storage.zone.region.getStoragecache() +} + +func (storage *SStorage) GetStatus() string { + return api.STORAGE_ONLINE +} + +func (region *SRegion) GetStorageTypes(zoneName string) ([]SStorage, error) { + params := url.Values{} + params.Set("clientToken", utils.GenRequestId(20)) + if len(zoneName) > 0 { + params.Set("zoneName", zoneName) + } + resp, err := region.bccList("v2/volume/disk", params) + if err != nil { + return nil, err + } + ret := struct { + DiskZoneResources []struct { + ZoneName string + DiskInfos []SStorage + } + }{} + err = resp.Unmarshal(&ret) + if err != nil { + return nil, err + } + storages := []SStorage{} + for _, diskInfos := range ret.DiskZoneResources { + storages = append(storages, diskInfos.DiskInfos...) + } + return storages, nil +} diff --git a/pkg/multicloud/baidu/storagecache.go b/pkg/multicloud/baidu/storagecache.go new file mode 100644 index 000000000..154c7f385 --- /dev/null +++ b/pkg/multicloud/baidu/storagecache.go @@ -0,0 +1,91 @@ +// 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 baidu + +import ( + "context" + "fmt" + + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/cloudmux/pkg/multicloud" +) + +type SStoragecache struct { + multicloud.SResourceBase + SBaiduTag + region *SRegion +} + +func (self *SStoragecache) GetId() string { + return fmt.Sprintf("%s-%s", self.region.client.cpcfg.Id, self.region.GetId()) +} + +func (self *SStoragecache) GetName() string { + return fmt.Sprintf("%s-%s", self.region.client.cpcfg.Name, self.region.GetId()) +} + +func (self *SStoragecache) GetStatus() string { + return "available" +} + +func (self *SStoragecache) Refresh() error { + return nil +} + +func (self *SStoragecache) GetGlobalId() string { + return fmt.Sprintf("%s-%s", self.region.client.cpcfg.Id, self.region.GetGlobalId()) +} + +func (self *SStoragecache) IsEmulated() bool { + return true +} + +func (self *SStoragecache) GetICloudImages() ([]cloudprovider.ICloudImage, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SStoragecache) GetICustomizedCloudImages() ([]cloudprovider.ICloudImage, error) { + images := []SImage{} + for _, imageType := range []string{"Custom", "Sharing"} { + part, err := self.region.GetImages(imageType) + if err != nil { + return nil, err + } + images = append(images, part...) + } + ret := []cloudprovider.ICloudImage{} + for i := range images { + images[i].cache = self + ret = append(ret, &images[i]) + } + return ret, nil +} + +func (self *SStoragecache) GetIImageById(extId string) (cloudprovider.ICloudImage, error) { + image, err := self.region.GetImage(extId) + if err != nil { + return nil, err + } + image.cache = self + return image, nil +} + +func (self *SStoragecache) GetPath() string { + return "" +} + +func (self *SStoragecache) UploadImage(ctx context.Context, image *cloudprovider.SImageCreateOption, callback func(float32)) (string, error) { + return "", cloudprovider.ErrNotImplemented +} diff --git a/pkg/multicloud/baidu/sts.go b/pkg/multicloud/baidu/sts.go new file mode 100644 index 000000000..377339455 --- /dev/null +++ b/pkg/multicloud/baidu/sts.go @@ -0,0 +1,39 @@ +// 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 baidu + +import "time" + +type SessionToken struct { + UserId string + SessionToken string + AccessKeyId string + SecretAccessKey string + CreateTime time.Time + Expiration time.Time +} + +func (client *SBaiduClient) GetSessionToken() (*SessionToken, error) { + resp, err := client.post(SERVICE_STS, "", "sessionToken", nil, nil) + if err != nil { + return nil, err + } + ret := &SessionToken{} + err = resp.Unmarshal(ret) + if err != nil { + return nil, err + } + return ret, nil +} diff --git a/pkg/multicloud/baidu/tag_base.go b/pkg/multicloud/baidu/tag_base.go new file mode 100644 index 000000000..8bebb42d4 --- /dev/null +++ b/pkg/multicloud/baidu/tag_base.go @@ -0,0 +1,40 @@ +// 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 baidu + +type BaiduTag struct { + TagKey string `json:"tagKey"` + TagValue string `json:"tagValue"` +} + +type SBaiduTag struct { + Tags []BaiduTag +} + +func (tag *SBaiduTag) GetTags() (map[string]string, error) { + res := map[string]string{} + for _, tagDetaoils := range tag.Tags { + res[tagDetaoils.TagKey] = tagDetaoils.TagValue + } + return res, nil +} + +func (tag *SBaiduTag) GetSysTags() map[string]string { + return nil +} + +func (tag *SBaiduTag) SetTags(tags map[string]string, replace bool) error { + return nil +} diff --git a/pkg/multicloud/baidu/vpcs.go b/pkg/multicloud/baidu/vpcs.go new file mode 100644 index 000000000..a7c005887 --- /dev/null +++ b/pkg/multicloud/baidu/vpcs.go @@ -0,0 +1,198 @@ +// 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 baidu + +import ( + "fmt" + "net/url" + + api "yunion.io/x/cloudmux/pkg/apis/compute" + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/cloudmux/pkg/multicloud" + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/utils" +) + +type SVpc struct { + multicloud.SVpc + SBaiduTag + + region *SRegion + + IsDefault bool `json:"IsDefault"` + VpcId string `json:"vpcId"` + Name string `json:"name"` + + CreateTime string `json:"CreateTime"` + Cidr string `json:"Cidr"` +} + +func (region *SRegion) GetVpcs() ([]SVpc, error) { + params := url.Values{} + vpcs := []SVpc{} + for { + resp, err := region.bccList("v1/vpc", params) + if err != nil { + return nil, errors.Wrap(err, "list vpcs") + } + part := struct { + Vpcs []SVpc + NextMarker string + }{} + err = resp.Unmarshal(&part) + if err != nil { + return nil, errors.Wrap(err, "Unmarshal") + } + for i := range part.Vpcs { + part.Vpcs[i].region = region + vpcs = append(vpcs, part.Vpcs[i]) + } + if len(part.NextMarker) == 0 { + break + } + params.Set("marker", part.NextMarker) + } + return vpcs, nil +} + +func (region *SRegion) GetVpc(vpcId string) (*SVpc, error) { + resp, err := region.bccList(fmt.Sprintf("v1/vpc/%s", vpcId), nil) + if err != nil { + return nil, errors.Wrap(err, "list vpcs") + } + vpc := SVpc{region: region} + err = resp.Unmarshal(&vpc, "vpc") + if err != nil { + return nil, errors.Wrap(err, "unmarshal vpc") + } + return &vpc, nil +} + +func (vpc *SVpc) GetId() string { + return vpc.VpcId +} + +func (vpc *SVpc) GetName() string { + if len(vpc.Name) > 0 { + return vpc.Name + } + return vpc.VpcId +} + +func (vpc *SVpc) GetGlobalId() string { + return vpc.VpcId +} + +func (vpc *SVpc) GetStatus() string { + return api.VPC_STATUS_AVAILABLE +} + +func (vpc *SVpc) Refresh() error { + res, err := vpc.region.GetVpc(vpc.GetId()) + if err != nil { + return err + } + return jsonutils.Update(vpc, res) +} + +func (vpc *SVpc) GetRegion() cloudprovider.ICloudRegion { + return vpc.region +} + +func (vpc *SVpc) GetIsDefault() bool { + return vpc.IsDefault +} + +func (vpc *SVpc) GetCidrBlock() string { + return vpc.Cidr +} + +func (vpc *SVpc) GetIWires() ([]cloudprovider.ICloudWire, error) { + zones, err := vpc.region.GetZones() + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudWire{} + for i := range zones { + wire := &SWire{vpc: vpc, zone: &zones[i]} + ret = append(ret, wire) + } + return ret, nil +} + +func (vpc *SVpc) GetISecurityGroups() ([]cloudprovider.ICloudSecurityGroup, error) { + groups, err := vpc.region.GetSecurityGroups(vpc.VpcId) + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudSecurityGroup{} + for i := range groups { + groups[i].region = vpc.region + ret = append(ret, &groups[i]) + } + return ret, nil +} + +func (vpc *SVpc) GetIRouteTables() ([]cloudprovider.ICloudRouteTable, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (vpc *SVpc) GetIRouteTableById(routeTableId string) (cloudprovider.ICloudRouteTable, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (vpc *SVpc) Delete() error { + return vpc.region.DeleteVpc(vpc.VpcId) +} + +func (vpc *SVpc) GetIWireById(id string) (cloudprovider.ICloudWire, error) { + wires, err := vpc.GetIWires() + if err != nil { + return nil, err + } + for i := range wires { + if wires[i].GetGlobalId() == id { + return wires[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + +func (region *SRegion) DeleteVpc(vpcId string) error { + params := url.Values{} + params.Set("clientToken", utils.GenRequestId(20)) + _, err := region.bccDelete(fmt.Sprintf("v1/vpc/%s", vpcId), params) + return err +} + +func (region *SRegion) CreateVpc(opts *cloudprovider.VpcCreateOptions) (*SVpc, error) { + params := url.Values{} + params.Set("clientToken", utils.GenRequestId(20)) + body := map[string]interface{}{ + "name": opts.NAME, + "description": opts.Desc, + "cidr": opts.CIDR, + } + resp, err := region.bccPost("v1/vpc", params, body) + if err != nil { + return nil, err + } + vpcId, err := resp.GetString("vpcId") + if err != nil { + return nil, err + } + return region.GetVpc(vpcId) +} diff --git a/pkg/multicloud/baidu/wire.go b/pkg/multicloud/baidu/wire.go new file mode 100644 index 000000000..2e95fc4e3 --- /dev/null +++ b/pkg/multicloud/baidu/wire.go @@ -0,0 +1,98 @@ +// 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 baidu + +import ( + "fmt" + + api "yunion.io/x/cloudmux/pkg/apis/compute" + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/cloudmux/pkg/multicloud" +) + +type SWire struct { + multicloud.SResourceBase + SBaiduTag + + zone *SZone + vpc *SVpc +} + +func (wire *SWire) GetId() string { + return fmt.Sprintf("%s-%s", wire.vpc.GetId(), wire.zone.GetId()) +} + +func (wire *SWire) GetName() string { + return wire.GetId() +} + +func (wire *SWire) IsEmulated() bool { + return true +} + +func (wire *SWire) GetStatus() string { + return api.WIRE_STATUS_AVAILABLE +} + +func (wire *SWire) Refresh() error { + return nil +} + +func (wire *SWire) GetGlobalId() string { + return fmt.Sprintf("%s-%s", wire.vpc.GetGlobalId(), wire.zone.GetGlobalId()) +} + +func (wire *SWire) GetIVpc() cloudprovider.ICloudVpc { + return wire.vpc +} + +func (wire *SWire) GetIZone() cloudprovider.ICloudZone { + return wire.zone +} + +func (wire *SWire) GetINetworks() ([]cloudprovider.ICloudNetwork, error) { + networks, err := wire.vpc.region.GetNetworks(wire.vpc.VpcId, wire.zone.ZoneName) + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudNetwork{} + for i := range networks { + networks[i].wire = wire + ret = append(ret, &networks[i]) + } + return ret, nil +} + +func (wire *SWire) GetBandwidth() int { + return 10000 +} + +func (wire *SWire) CreateINetwork(opts *cloudprovider.SNetworkCreateOptions) (cloudprovider.ICloudNetwork, error) { + net, err := wire.vpc.region.CreateNetwork(wire.zone.ZoneName, wire.vpc.VpcId, opts) + if err != nil { + return nil, err + } + net.wire = wire + return net, nil +} + +func (wire *SWire) GetINetworkById(id string) (cloudprovider.ICloudNetwork, error) { + net, err := wire.vpc.region.GetNetwork(id) + if err != nil { + return nil, err + } + net.wire = wire + return net, nil +} diff --git a/pkg/multicloud/baidu/zone.go b/pkg/multicloud/baidu/zone.go new file mode 100644 index 000000000..852440388 --- /dev/null +++ b/pkg/multicloud/baidu/zone.go @@ -0,0 +1,141 @@ +// 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 baidu + +import ( + "fmt" + + api "yunion.io/x/cloudmux/pkg/apis/compute" + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/cloudmux/pkg/multicloud" + "yunion.io/x/pkg/errors" +) + +type SZone struct { + multicloud.SResourceBase + SBaiduTag + region *SRegion + + ZoneName string `json:"zoneName"` +} + +func (zone *SZone) GetId() string { + return zone.ZoneName +} + +func (zone *SZone) GetGlobalId() string { + return fmt.Sprintf("%s/%s", zone.region.GetGlobalId(), zone.ZoneName) +} + +func (zone *SZone) GetName() string { + return zone.ZoneName +} + +func (zone *SZone) GetStatus() string { + return api.ZONE_ENABLE +} + +func (zone *SZone) IsEmulated() bool { + return false +} + +func (zone *SZone) Refresh() error { + return nil +} + +func (zone *SZone) GetIRegion() cloudprovider.ICloudRegion { + return zone.region +} + +func (zone *SZone) GetI18n() cloudprovider.SModelI18nTable { + table := cloudprovider.SModelI18nTable{} + return table +} + +func (region *SRegion) GetZones() ([]SZone, error) { + resp, err := region.bccList("v2/zone", nil) + if err != nil { + return nil, err + } + ret := []SZone{} + err = resp.Unmarshal(&ret, "zones") + if err != nil { + return nil, err + } + for i := range ret { + ret[i].region = region + } + return ret, nil +} + +func (zone *SZone) GetIStorages() ([]cloudprovider.ICloudStorage, error) { + storages, err := zone.region.GetStorageTypes(zone.ZoneName) + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudStorage{} + for i := range storages { + storages[i].zone = zone + ret = append(ret, &storages[i]) + } + return ret, nil +} + +func (zone *SZone) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { + storages, err := zone.GetIStorages() + if err != nil { + return nil, err + } + for i := range storages { + if storages[i].GetGlobalId() == id { + return storages[i], nil + } + } + return nil, errors.Wrapf(cloudprovider.ErrNotFound, id) +} + +func (zone *SZone) getHost() *SHost { + return &SHost{zone: zone} +} + +func (zone *SZone) GetIHosts() ([]cloudprovider.ICloudHost, error) { + return []cloudprovider.ICloudHost{zone.getHost()}, nil +} + +func (zone *SZone) GetIHostById(id string) (cloudprovider.ICloudHost, error) { + hosts, err := zone.GetIHosts() + if err != nil { + return nil, err + } + for i := range hosts { + if hosts[i].GetGlobalId() == id { + return hosts[i], nil + } + } + return nil, cloudprovider.ErrNotFound +} + +func (zone *SZone) GetIWires() ([]cloudprovider.ICloudWire, error) { + vpcs, err := zone.region.GetVpcs() + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudWire{} + for i := range vpcs { + wire := &SWire{zone: zone, vpc: &vpcs[i]} + ret = append(ret, wire) + } + return ret, nil +}