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 f2966c285..d86fed7a7 100644 --- a/pkg/multicloud/baidu/baidu.go +++ b/pkg/multicloud/baidu/baidu.go @@ -37,6 +37,13 @@ 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_BILLING = "billing" ) type BaiduClientConfig struct { @@ -85,27 +92,33 @@ func NewBaiduClient(cfg *BaiduClientConfig) (*SBaiduClient, error) { return client, err } -func (cli *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: cli, - 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 (cli *SBaiduClient) GetRegion(id string) (*SRegion, error) { - regions := cli.GetRegions() + regions, err := cli.GetRegions() + if err != nil { + return nil, err + } for i := range regions { - if regions[i].Region == id { - regions[i].client = cli + 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 (cli *SBaiduClient) getUrl(service, regionId, resource string) (string, error) { @@ -113,14 +126,18 @@ func (cli *SBaiduClient) getUrl(service, regionId, resource string) (string, err 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 "bcc": + case SERVICE_BCC: return fmt.Sprintf("https://bcc.%s.baidubce.com/%s", regionId, strings.TrimPrefix(resource, "/")), nil - case "bos": + 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 default: return "", errors.Wrapf(cloudprovider.ErrNotSupported, service) } @@ -164,6 +181,9 @@ func (e *sBaiduError) ParseErrorFromJsonResponse(statusCode int, status string, body.Unmarshal(e) } e.StatusCode = statusCode + if e.StatusCode == 404 { + return errors.Wrapf(cloudprovider.ErrNotFound, e.Error()) + } return e } @@ -183,34 +203,66 @@ func (cli *SBaiduClient) Do(req *http.Request) (*http.Response, error) { return client.Do(req) } -func (cli *SBaiduClient) bccList(regionId, resource string, params map[string]interface{}) (jsonutils.JSONObject, error) { - return cli.list("bcc", 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 (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 (cli *SBaiduClient) eipDelete(regionId, resource string, params url.Values) (jsonutils.JSONObject, error) { + return cli.delete(SERVICE_EIP, regionId, resource, params) } -func (cli *SBaiduClient) list(service, regionId, resource string, params map[string]interface{}) (jsonutils.JSONObject, error) { - return cli.request(httputils.GET, service, 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) post(service, regionId, resource string, params map[string]interface{}) (jsonutils.JSONObject, error) { - return cli.request(httputils.POST, service, regionId, resource, params) +func (cli *SBaiduClient) bccList(regionId, resource string, params url.Values) (jsonutils.JSONObject, error) { + return cli.list(SERVICE_BCC, regionId, resource, params) } -func (cli *SBaiduClient) request(method httputils.THttpMethod, service, regionId, resource string, params map[string]interface{}) (jsonutils.JSONObject, error) { +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) 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) + req := httputils.NewJsonRequest(method, uri, body) bErr := &sBaiduError{} client := httputils.NewJsonClient(cli) _, resp, err := client.Send(cli.ctx, req, bErr, cli.debug) @@ -230,12 +282,12 @@ func (cli *SBaiduClient) getOwnerId() (string, error) { if len(cli.ownerId) > 0 { return cli.ownerId, nil } - resp, err := cli.list("bos", "bj", "/", nil) + session, err := cli.GetSessionToken() if err != nil { return "", err } - cli.ownerId, err = resp.GetString("owner", "id") - return cli.ownerId, err + cli.ownerId = session.UserId + return cli.ownerId, nil } func (cli *SBaiduClient) GetAccountId() string { @@ -248,7 +300,7 @@ type CashBalance struct { } func (cli *SBaiduClient) QueryBalance() (*CashBalance, error) { - resp, err := cli.post("billing", "", "/v1/finance/cash/balance", nil) + resp, err := cli.post("billing", "", "/v1/finance/cash/balance", nil, nil) if err != nil { return nil, err } @@ -262,7 +314,11 @@ func (cli *SBaiduClient) QueryBalance() (*CashBalance, error) { 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 index f6e4693e4..8f0651a89 100644 --- a/pkg/multicloud/baidu/disk.go +++ b/pkg/multicloud/baidu/disk.go @@ -17,26 +17,38 @@ 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 `json:"InstanceId"` - MountPoint string `json:"MountPoint"` - DeleteWithInstance bool `json:"DeleteWithInstance"` + InstanceId string + MountPoint string + DeleteWithInstance bool } -type HistoryAttachment struct { - InstanceID string `json:"InstanceId"` - AttachTime string `json:"AttachTime"` - DetachTime string `json:"DetachTime"` - MountPoint string `json:"MountPoint"` +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 { @@ -44,109 +56,77 @@ type SDisk struct { multicloud.SDisk SBaiduTag - Id string `json:"id"` - CreateTime time.Time `json:"createTime"` - ExpireTime interface{} `json:"expireTime"` - Name string `json:"name"` - DiskSizeInGB int `json:"diskSizeInGB"` - Status string `json:"status"` - Type string `json:"type"` - StorageType string `json:"storageType"` - Desc string `json:"desc"` - PaymentTiming string `json:"paymentTiming"` - Attachments []Attachments `json:"attachments"` - RegionID string `json:"regionId"` - SourceSnapshotID string `json:"sourceSnapshotId"` - SnapshotNum string `json:"snapshotNum"` - Tags []Tags `json:"tags"` - AutoSnapshotPolicy AutoSnapshotPolicy `json:"autoSnapshotPolicy"` - ZoneName string `json:"zoneName"` - IsSystemVolume bool `json:"isSystemVolume"` -} - -type Attachments struct { - Id string `json:"Id"` - InstanceID string `json:"instanceId"` - Device string `json:"device"` - Serial string `json:"serial"` -} - -type AutoSnapshotPolicy struct { - ID string `json:"id"` - Name string `json:"name"` - TimePoints []int `json:"timePoints"` - RepeatWeekdays []int `json:"repeatWeekdays"` - RetentionDays int `json:"retentionDays"` - Status string `json:"status"` -} - -func (region *SRegion) GetDisks(storageType, zoneName string) ([]SDisk, error) { - params := map[string]interface{}{} + 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["zoneName"] = zoneName + params.Set("zoneName", zoneName) } if len(storageType) > 0 { - params["storageType"] = storageType + params.Set("storageType", storageType) + } + if len(instanceId) > 0 { + params.Set("instanceId", instanceId) } disks := []SDisk{} for { - resp, err := region.client.bccList(region.Region, "v2/volume", params) + resp, err := region.bccList("v2/volume", params) if err != nil { return nil, errors.Wrap(err, "list disks") } - temp := []SDisk{} - err = resp.Unmarshal(&temp, "volumes") + part := struct { + NextMarker string + Volumes []SDisk + }{} + err = resp.Unmarshal(&part) if err != nil { - return nil, errors.Wrap(err, "unmarshal disks") + return nil, err } - disks = append(disks, temp...) - if nextMarker, _ := resp.GetString("nextMarker"); len(nextMarker) > 0 { - params["marker"] = nextMarker - } else { + disks = append(disks, part.Volumes...) + if len(part.NextMarker) == 0 { break } + params.Set("marker", part.NextMarker) } return disks, nil } func (region *SRegion) GetDisk(diskId string) (*SDisk, error) { - params := map[string]interface{}{} - disk := SDisk{} - resp, err := region.client.bccList(region.Region, fmt.Sprintf("v2/volume/%s", diskId), params) + resp, err := region.bccList(fmt.Sprintf("v2/volume/%s", diskId), nil) if err != nil { return nil, errors.Wrap(err, "list disks") } - err = resp.Unmarshal(&disk, "volume") + ret := &SDisk{} + err = resp.Unmarshal(ret, "volume") if err != nil { return nil, errors.Wrap(err, "unmarshal disks") } - return &disk, nil -} - -func (region *SRegion) GetDiskByInstanceId(instanceId string) ([]SDisk, error) { - disks := []SDisk{} - params := map[string]interface{}{ - "MaxResults": "1000", - } - params["InstanceId"] = instanceId - resp, err := region.client.bccList(region.Region, "DescribeInstanceVolumes", params) - if err != nil { - return nil, errors.Wrap(err, "DescribeInstanceVolumes") - } - return disks, resp.Unmarshal(&disks, "Attachments") + return ret, nil } func (disk *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) { - if disk.storage == nil { - return nil, fmt.Errorf("disk %s(%s) missing storage", disk.Name, disk.Id) - } return disk.storage, nil } func (disk *SDisk) GetIStorageId() string { - if disk.storage == nil { - return "" - } return disk.storage.GetGlobalId() } @@ -167,14 +147,23 @@ func (disk *SDisk) GetName() string { } func (disk *SDisk) GetStatus() string { - // creating、available、attaching、inuse、detaching、extending、deleting、error switch disk.Status { - case "Available", "InUse": + case "Available", "InUse", "Recharging": return api.DISK_READY case "Detaching": return api.DISK_DETACHING - case "Error": + 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) } @@ -227,16 +216,37 @@ 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 cloudprovider.ErrNotSupported + return disk.storage.zone.region.DeleteDisk(disk.Id) } func (disk *SDisk) CreateISnapshot(ctx context.Context, name string, desc string) (cloudprovider.ICloudSnapshot, error) { - return nil, cloudprovider.ErrNotSupported + 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) { - return nil, cloudprovider.ErrNotSupported + 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) { @@ -244,17 +254,66 @@ func (disk *SDisk) GetExtSnapshotPolicyIds() ([]string, error) { } func (disk *SDisk) Resize(ctx context.Context, newSizeMB int64) error { - return cloudprovider.ErrNotSupported + return disk.storage.zone.region.ResizeDisk(disk.Id, newSizeMB/1024) } func (disk *SDisk) Reset(ctx context.Context, snapshotId string) (string, error) { - return "", cloudprovider.ErrNotSupported + 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 (disk *SDisk) SetStorage(storage SStorage) { - disk.storage = &storage +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..7e1d9c3e0 --- /dev/null +++ b/pkg/multicloud/baidu/eip.go @@ -0,0 +1,274 @@ +// 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) + } + 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 index 3cfcc0870..ef4fa8564 100644 --- a/pkg/multicloud/baidu/host.go +++ b/pkg/multicloud/baidu/host.go @@ -15,6 +15,9 @@ 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/jsonutils" @@ -24,8 +27,6 @@ import ( type SHost struct { multicloud.SHostBase zone *SZone - - projectId string } func (host *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) { @@ -54,7 +55,7 @@ func (host *SHost) GetAccessMac() string { } func (host *SHost) GetName() string { - return "" + return fmt.Sprintf("%s-%s", host.zone.region.client.cpcfg.Name, host.zone.GetId()) } func (host *SHost) GetNodeCount() int8 { @@ -66,7 +67,7 @@ func (host *SHost) GetSN() string { } func (host *SHost) GetStatus() string { - return "" + return api.HOST_STATUS_RUNNING } func (host *SHost) GetCpuCount() int { @@ -94,47 +95,60 @@ func (host *SHost) GetStorageClass() string { } func (host *SHost) GetStorageType() string { - return "" + return api.DISK_TYPE_HYBRID } func (host *SHost) GetEnabled() bool { - return false + return true } func (host *SHost) GetIsMaintenance() bool { return false } +func (host *SHost) IsEmulated() bool { + return true +} + func (host *SHost) GetGlobalId() string { - return "" + return fmt.Sprintf("%s-%s", host.zone.region.client.cpcfg.Id, host.zone.GetId()) } func (host *SHost) GetId() string { - return "" + return fmt.Sprintf("%s-%s", host.zone.region.client.cpcfg.Id, host.zone.GetId()) } func (host *SHost) GetHostStatus() string { - return "" + return api.HOST_ONLINE } func (host *SHost) GetHostType() string { - return "" + return api.HOST_TYPE_BAIDU } func (host *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) { - return nil, nil + wires, err := host.zone.GetIWires() + if err != nil { + return nil, errors.Wrap(err, "GetIWires") + } + return cloudprovider.GetHostNetifs(host, wires), nil } -func (host *SHost) GetIStorageById(storageId string) (cloudprovider.ICloudStorage, error) { - return nil, nil +func (host *SHost) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { + return host.zone.GetIStorageById(id) } func (host *SHost) GetIStorages() ([]cloudprovider.ICloudStorage, error) { - return nil, nil + return host.zone.GetIStorages() } func (host *SHost) GetIVMById(vmId string) (cloudprovider.ICloudVM, error) { - return nil, nil + 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 { diff --git a/pkg/multicloud/baidu/image.go b/pkg/multicloud/baidu/image.go new file mode 100644 index 000000000..7b012b29a --- /dev/null +++ b/pkg/multicloud/baidu/image.go @@ -0,0 +1,228 @@ +// 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 (self *SImage) getNormalizedImageInfo() *imagetools.ImageInfo { + if self.imgInfo == nil { + imgInfo := imagetools.NormalizeImageInfo(self.Name, self.OsArch, self.OsName, self.OsType, 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 index 91b4ef34b..9121e2308 100644 --- a/pkg/multicloud/baidu/instance.go +++ b/pkg/multicloud/baidu/instance.go @@ -17,6 +17,7 @@ package baidu import ( "context" "fmt" + "net/url" "strings" "time" @@ -24,6 +25,7 @@ import ( "yunion.io/x/cloudmux/pkg/cloudprovider" "yunion.io/x/cloudmux/pkg/multicloud" "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/utils" ) type SInstance struct { @@ -31,146 +33,173 @@ type SInstance struct { SBaiduTag host *SHost - Id string `json:"id"` - Name string `json:"name"` - RoleName string `json:"roleName"` - Hostname string `json:"hostname"` - InstanceType string `json:"instanceType"` - Spec string `json:"spec"` - Status string `json:"status"` - Desc string `json:"desc"` - CreatedFrom string `json:"createdFrom"` - PaymentTiming string `json:"paymentTiming"` - CreateTime time.Time `json:"createTime"` - InternalIP string `json:"internalIp"` - PublicIP string `json:"publicIp"` - CPUCount int `json:"cpuCount"` - IsomerismCard string `json:"isomerismCard"` - CardCount string `json:"cardCount"` - NpuVideoMemory string `json:"npuVideoMemory"` - MemoryCapacityInGB int `json:"memoryCapacityInGB"` - LocalDiskSizeInGB int `json:"localDiskSizeInGB"` - ImageID string `json:"imageId"` - ImageName string `json:"imageName"` - ImageType string `json:"imageType"` - PlacementPolicy string `json:"placementPolicy"` - SubnetID string `json:"subnetId"` - VpcID string `json:"vpcId"` - HostID string `json:"hostId"` - SwitchID string `json:"switchId"` - RackID string `json:"rackId"` - DeploysetID string `json:"deploysetId"` - ZoneName string `json:"zoneName"` - DedicatedHostID string `json:"dedicatedHostId"` - OsVersion string `json:"osVersion"` - OsArch string `json:"osArch"` - OsName string `json:"osName"` - HosteyeType string `json:"hosteyeType"` - DeploysetList []interface{} `json:"deploysetList"` - NicInfo NicInfo `json:"nicInfo"` - DeletionProtection int `json:"deletionProtection"` - Ipv6 string `json:"ipv6"` - Tags []Tags `json:"tags"` - Volumes []SDisk `json:"volumes"` - NetworkCapacityInMbps int `json:"networkCapacityInMbps"` -} - -type Ips struct { - PrivateIP string `json:"privateIp"` - Eip string `json:"eip"` - Primary string `json:"primary"` - EipID string `json:"eipId"` - EipAllocationID string `json:"eipAllocationId"` - EipSize string `json:"eipSize"` - EipStatus string `json:"eipStatus"` - EipGroupID string `json:"eipGroupId"` - EipType string `json:"eipType"` -} -type NicInfo struct { - EniID string `json:"eniId"` - EniUUID string `json:"eniUuid"` - Name string `json:"name"` - Type string `json:"type"` - SubnetID string `json:"subnetId"` - SubnetType string `json:"subnetType"` - Az string `json:"az"` - Description string `json:"description"` - DeviceID string `json:"deviceId"` - Status string `json:"status"` - MacAddress string `json:"macAddress"` - VpcID string `json:"vpcId"` - CreatedTime string `json:"createdTime"` - EniNum int `json:"eniNum"` - EriNum int `json:"eriNum"` - EriInfos []interface{} `json:"eriInfos"` - Ips []Ips `json:"ips"` - SecurityGroups []string `json:"securityGroups"` -} -type Tags struct { - TagKey string `json:"tagKey"` - TagValue string `json:"tagValue"` + 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 := map[string]interface{}{} + params := url.Values{} if len(zoneName) > 0 { - params["zoneName"] = zoneName + params.Set("zoneName", zoneName) } if len(instanceIds) > 0 { - params["instanceIds"] = strings.Join(instanceIds, ",") + params.Set("instanceIds", strings.Join(instanceIds, ",")) } - instances := []SInstance{} + ret := []SInstance{} for { - resp, err := region.client.bccList(region.Region, "v2/instance", params) + resp, err := region.bccList("v2/instance", params) if err != nil { return nil, errors.Wrap(err, "list instance") } - temp := []SInstance{} - err = resp.Unmarshal(&temp, "instances") + part := struct { + NextMarker string + Instances []SInstance + }{} + err = resp.Unmarshal(&part) if err != nil { - return nil, errors.Wrap(err, "unmarshal instances") + return nil, err } - instances = append(instances, temp...) - if nextMarker, _ := resp.GetString("nextMarker"); len(nextMarker) > 0 { - params["marker"] = nextMarker - } else { + ret = append(ret, part.Instances...) + if len(part.NextMarker) == 0 { break } + params.Set("marker", part.NextMarker) } - return instances, nil + return ret, nil } func (region *SRegion) GetInstance(instanceId string) (*SInstance, error) { - instance := &SInstance{} - resp, err := region.client.bccList(region.Region, fmt.Sprintf("v2/instance/%s", instanceId), nil) + resp, err := region.bccList(fmt.Sprintf("v2/instance/%s", instanceId), nil) if err != nil { return nil, errors.Wrap(err, "show instance") } - err = resp.Unmarshal(instance, "instance") + ret := &SInstance{} + err = resp.Unmarshal(ret, "instance") if err != nil { return nil, errors.Wrap(err, "unmarshal instance") } - return instance, nil + return ret, nil } func (ins *SInstance) AssignSecurityGroup(secgroupId string) error { - return errors.ErrNotImplemented + 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 errors.ErrNotImplemented + return ins.host.zone.region.AttachDisk(ins.Id, diskId) } -func (ins *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error { - return errors.ErrNotImplemented +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 errors.ErrNotImplemented + return ins.host.zone.region.DeleteVm(ins.Id) +} + +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 errors.ErrNotImplemented + 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 { @@ -237,7 +266,7 @@ func (ins *SInstance) GetOsVersion() string { } func (ins *SInstance) GetProjectId() string { - return ins.host.projectId + return "" } func (ins *SInstance) GetSecurityGroupIds() ([]string, error) { @@ -263,19 +292,56 @@ func (ins *SInstance) GetHypervisor() string { } func (ins *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) { - return nil, nil + 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 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) { - return nil, nil + 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 nil, nil + return []cloudprovider.ICloudNic{&ins.NicInfo}, nil } func (ins *SInstance) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) { - return nil, cloudprovider.ErrNotImplemented + 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 { @@ -294,34 +360,159 @@ func (ins *SInstance) GetVga() string { return "" } -func (ins *SInstance) RebuildRoot(ctx context.Context, config *cloudprovider.SManagedVMRebuildRootConfig) (string, error) { - return "", cloudprovider.ErrNotImplemented +func (ins *SInstance) RebuildRoot(ctx context.Context, opts *cloudprovider.SManagedVMRebuildRootConfig) (string, error) { + err := ins.host.zone.region.RebuildRoot(ins.Id, opts) + if err != nil { + return "", err + } + return "", nil +} + +func (region *SRegion) RebuildRoot(vmId string, opts *cloudprovider.SManagedVMRebuildRootConfig) error { + params := url.Values{} + params.Set("rebuild", "") + body := map[string]interface{}{ + "imageId": opts.ImageId, + "adminPass": opts.Password, + "userData": opts.UserData, + } + _, err := region.bccUpdate(fmt.Sprintf("v2/instance/%s", vmId), params, body) + return err } func (ins *SInstance) SetSecurityGroups(secgroupIds []string) error { - return cloudprovider.ErrNotImplemented + 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 cloudprovider.ErrNotImplemented + 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 cloudprovider.ErrNotImplemented + 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.ErrNotImplemented + return cloudprovider.ErrNotSupported } func (ins *SInstance) UpdateVM(ctx context.Context, input cloudprovider.SInstanceUpdateOptions) error { - return cloudprovider.ErrNotImplemented + 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 { - return cloudprovider.ErrNotImplemented + 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..864bde96a --- /dev/null +++ b/pkg/multicloud/baidu/instancenic.go @@ -0,0 +1,85 @@ +// 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) GetIP6() string { + return "" +} + +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/monitor.go b/pkg/multicloud/baidu/monitor.go new file mode 100644 index 000000000..5f6259ea8 --- /dev/null +++ b/pkg/multicloud/baidu/monitor.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 baidu + +import ( + "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) + } +} + +func (self *SBaiduClient) GetEcsMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) { + ret := []cloudprovider.MetricValues{} + return ret, cloudprovider.ErrNotImplemented +} 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 5ed6df69f..cd5d72384 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,16 +43,17 @@ type SRegion struct { multicloud.SNoLbRegion client *SBaiduClient - Region string - RegionName string + RegionId string + RegionName string + RegionEndpoint string } func (region *SRegion) GetId() string { - return region.Region + return region.RegionId } func (region *SRegion) GetGlobalId() string { - return fmt.Sprintf("%s/%s", api.CLOUD_PROVIDER_BAIDU, region.Region) + return fmt.Sprintf("%s/%s", api.CLOUD_PROVIDER_BAIDU, region.RegionId) } func (region *SRegion) GetProvider() string { @@ -69,7 +74,7 @@ func (region *SRegion) GetGeographicInfo() cloudprovider.SGeographicInfo { "bd": api.RegionBaoDing, "sin": api.RegionSingapore, "fsh": api.RegionShanghai, - }[region.Region] + }[region.RegionId] if ok { return geo } @@ -77,12 +82,15 @@ func (region *SRegion) GetGeographicInfo() cloudprovider.SGeographicInfo { } func (region *SRegion) GetName() string { + if name, ok := regions[region.RegionId]; ok { + return name + } return region.RegionName } func (region *SRegion) GetI18n() cloudprovider.SModelI18nTable { table := cloudprovider.SModelI18nTable{} - table["name"] = cloudprovider.NewSModelI18nEntry(region.GetName()).CN(region.GetName()).EN(region.Region) + table["name"] = cloudprovider.NewSModelI18nEntry(region.GetName()).CN(region.GetName()).EN(region.RegionName) return table } @@ -95,45 +103,243 @@ func (region *SRegion) GetClient() *SBaiduClient { } func (region *SRegion) CreateEIP(opts *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) { - return nil, cloudprovider.ErrNotImplemented + 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 (region *SRegion) CreateIVpc(opts *cloudprovider.VpcCreateOptions) (cloudprovider.ICloudVpc, error) { - return nil, cloudprovider.ErrNotImplemented + vpc, err := region.CreateVpc(opts) + if err != nil { + return nil, err + } + return vpc, nil } func (region *SRegion) GetIVpcs() ([]cloudprovider.ICloudVpc, error) { - return nil, cloudprovider.ErrNotImplemented + 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 (region *SRegion) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) { - return nil, cloudprovider.ErrNotImplemented + vpc, err := region.GetVpc(id) + if err != nil { + return nil, err + } + return vpc, nil } func (region *SRegion) GetCapabilities() []string { return region.client.GetCapabilities() } -func (region *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) { - return nil, cloudprovider.ErrNotImplemented + 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) { - return nil, cloudprovider.ErrNotImplemented + 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) { - return nil, cloudprovider.ErrNotImplemented + 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 (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 (region *SRegion) eipDelete(resource string, params url.Values) (jsonutils.JSONObject, error) { + return region.client.eipDelete(region.RegionId, resource, params) +} + +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 index 8d4b355e3..11f6b676f 100644 --- a/pkg/multicloud/baidu/shell/disk.go +++ b/pkg/multicloud/baidu/shell/disk.go @@ -25,10 +25,14 @@ 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, _ := cli.GetDisks(args.StorageType, args.ZoneName) - printList(disks, 0, 0, 0, []string{}) + disks, err := cli.GetDisks(args.StorageType, args.ZoneName, args.InstanceId) + if err != nil { + return err + } + printList(disks) return nil }) type diskShowOptions struct { 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 index d84a327af..d1db8e6d5 100644 --- a/pkg/multicloud/baidu/shell/instances.go +++ b/pkg/multicloud/baidu/shell/instances.go @@ -31,7 +31,7 @@ func init() { if err != nil { return errors.Wrap(err, "GetInstances") } - printList(res, 0, 0, 0, []string{}) + printList(res) 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 index 63898b90b..88f043a91 100644 --- a/pkg/multicloud/baidu/shell/vpc.go +++ b/pkg/multicloud/baidu/shell/vpc.go @@ -18,6 +18,7 @@ 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" ) @@ -25,14 +26,17 @@ func init() { type VpcListOptions struct { } shellutils.R(&VpcListOptions{}, "vpc-list", "list vpcs", func(cli *baidu.SRegion, args *VpcListOptions) error { - vpcs, _ := cli.GetVpcs() - printList(vpcs, 0, 0, 0, []string{}) + vpcs, err := cli.GetVpcs() + if err != nil { + return err + } + printList(vpcs) return nil }) - type VpcShowOptions struct { + type VpcIdOptions struct { ID string `help:"ID of vpc to show"` } - shellutils.R(&VpcShowOptions{}, "vpc-show", "list vpcs", func(cli *baidu.SRegion, args *VpcShowOptions) error { + 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") @@ -40,4 +44,18 @@ func init() { 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/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/storage.go b/pkg/multicloud/baidu/storage.go index a7af28798..ce66d911a 100644 --- a/pkg/multicloud/baidu/storage.go +++ b/pkg/multicloud/baidu/storage.go @@ -16,6 +16,7 @@ package baidu import ( "fmt" + "net/url" "strings" api "yunion.io/x/cloudmux/pkg/apis/compute" @@ -23,6 +24,7 @@ import ( "yunion.io/x/cloudmux/pkg/multicloud" "yunion.io/x/jsonutils" "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/utils" ) type SStorage struct { @@ -30,6 +32,8 @@ type SStorage struct { zone *SZone StorageType string + MinDiskSize int + MaxDiskSize int } func (storage *SStorage) GetId() string { @@ -49,11 +53,11 @@ func (storage *SStorage) IsEmulated() bool { } func (storage *SStorage) GetIZone() cloudprovider.ICloudZone { - return nil + return storage.zone } func (storage *SStorage) GetIDisks() ([]cloudprovider.ICloudDisk, error) { - disks, err := storage.zone.region.GetDisks(storage.StorageType, storage.zone.GetId()) + disks, err := storage.zone.region.GetDisks(storage.StorageType, storage.zone.ZoneName, "") if err != nil { return nil, errors.Wrap(err, "region.GetDisks") } @@ -70,11 +74,10 @@ func (storage *SStorage) GetStorageType() string { } func (storage *SStorage) GetMediumType() string { - if strings.Contains(storage.StorageType, "SSD") { + if strings.Contains(strings.ToLower(storage.StorageType), "ssd") { return api.DISK_TYPE_SSD - } else { - return api.DISK_TYPE_ROTATE } + return api.DISK_TYPE_ROTATE } func (storage *SStorage) GetCapacityMB() int64 { @@ -93,12 +96,22 @@ func (storage *SStorage) GetEnabled() bool { return true } -func (storage *SStorage) CreateIDisk(conf *cloudprovider.DiskCreateConfig) (cloudprovider.ICloudDisk, error) { - return nil, cloudprovider.ErrNotSupported +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) { - return nil, errors.ErrNotImplemented + disk, err := storage.zone.region.GetDisk(id) + if err != nil { + return nil, err + } + disk.storage = storage + return disk, nil } func (storage *SStorage) GetMountPoint() string { @@ -114,9 +127,36 @@ func (storage *SStorage) DisableSync() bool { } func (storage *SStorage) GetIStoragecache() cloudprovider.ICloudStoragecache { - return nil + 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 index f144b59a2..8bebb42d4 100644 --- a/pkg/multicloud/baidu/tag_base.go +++ b/pkg/multicloud/baidu/tag_base.go @@ -14,19 +14,13 @@ package baidu -type SBaiduTag struct { - Tags []struct { - TagKey string `json:"tagKey"` - TagValue string `json:"tagValue"` - } +type BaiduTag struct { + TagKey string `json:"tagKey"` + TagValue string `json:"tagValue"` } -func (tag SBaiduTag) GetName() string { - return "" -} - -func (tag SBaiduTag) GetDescription() string { - return "" +type SBaiduTag struct { + Tags []BaiduTag } func (tag *SBaiduTag) GetTags() (map[string]string, error) { diff --git a/pkg/multicloud/baidu/vpcs.go b/pkg/multicloud/baidu/vpcs.go index bea29de16..a7c005887 100644 --- a/pkg/multicloud/baidu/vpcs.go +++ b/pkg/multicloud/baidu/vpcs.go @@ -16,19 +16,16 @@ 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 SVpcResp struct { - Vpcs []SVpc `json:"vpcs"` - IsTruncated bool `json:"isTruncated"` -} - type SVpc struct { multicloud.SVpc SBaiduTag @@ -36,7 +33,7 @@ type SVpc struct { region *SRegion IsDefault bool `json:"IsDefault"` - VpcID string `json:"vpcId"` + VpcId string `json:"vpcId"` Name string `json:"name"` CreateTime string `json:"CreateTime"` @@ -44,24 +41,39 @@ type SVpc struct { } func (region *SRegion) GetVpcs() ([]SVpc, error) { - resp, err := region.client.bccList(region.Region, "v1/vpc", nil) - if err != nil { - return nil, errors.Wrap(err, "list vpcs") - } + params := url.Values{} vpcs := []SVpc{} - err = resp.Unmarshal(&vpcs, "vpcs") - if err != nil { - return nil, errors.Wrap(err, "unmarshal instances") + 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.client.bccList(region.Region, fmt.Sprintf("v1/vpc/%s", vpcId), nil) + resp, err := region.bccList(fmt.Sprintf("v1/vpc/%s", vpcId), nil) if err != nil { return nil, errors.Wrap(err, "list vpcs") } - vpc := SVpc{} + vpc := SVpc{region: region} err = resp.Unmarshal(&vpc, "vpc") if err != nil { return nil, errors.Wrap(err, "unmarshal vpc") @@ -70,18 +82,18 @@ func (region *SRegion) GetVpc(vpcId string) (*SVpc, error) { } func (vpc *SVpc) GetId() string { - return vpc.VpcID + return vpc.VpcId } func (vpc *SVpc) GetName() string { if len(vpc.Name) > 0 { return vpc.Name } - return vpc.VpcID + return vpc.VpcId } func (vpc *SVpc) GetGlobalId() string { - return vpc.VpcID + return vpc.VpcId } func (vpc *SVpc) GetStatus() string { @@ -89,15 +101,11 @@ func (vpc *SVpc) GetStatus() string { } func (vpc *SVpc) Refresh() error { - new, err := vpc.region.GetVpc(vpc.GetId()) + res, err := vpc.region.GetVpc(vpc.GetId()) if err != nil { return err } - return jsonutils.Update(vpc, new) -} - -func (vpc *SVpc) IsEmulated() bool { - return false + return jsonutils.Update(vpc, res) } func (vpc *SVpc) GetRegion() cloudprovider.ICloudRegion { @@ -113,11 +121,29 @@ func (vpc *SVpc) GetCidrBlock() string { } func (vpc *SVpc) GetIWires() ([]cloudprovider.ICloudWire, error) { - return nil, cloudprovider.ErrNotImplemented + 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) { - return nil, cloudprovider.ErrNotImplemented + 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) { @@ -129,41 +155,44 @@ func (vpc *SVpc) GetIRouteTableById(routeTableId string) (cloudprovider.ICloudRo } func (vpc *SVpc) Delete() error { - return cloudprovider.ErrNotImplemented -} - -func (vpc *SVpc) GetIWireById(wireId string) (cloudprovider.ICloudWire, error) { - return nil, cloudprovider.ErrNotImplemented -} - -func (vpc *SVpc) GetINatGateways() ([]cloudprovider.ICloudNatGateway, error) { - return nil, cloudprovider.ErrNotImplemented - -} - -func (vpc *SVpc) GetICloudVpcPeeringConnections() ([]cloudprovider.ICloudVpcPeeringConnection, error) { - return nil, cloudprovider.ErrNotImplemented -} - -func (vpc *SVpc) GetICloudAccepterVpcPeeringConnections() ([]cloudprovider.ICloudVpcPeeringConnection, error) { - return nil, cloudprovider.ErrNotImplemented -} - -func (vpc *SVpc) GetICloudVpcPeeringConnectionById(id string) (cloudprovider.ICloudVpcPeeringConnection, error) { - return nil, cloudprovider.ErrNotImplemented + return vpc.region.DeleteVpc(vpc.VpcId) } -func (vpc *SVpc) CreateICloudVpcPeeringConnection(opts *cloudprovider.VpcPeeringConnectionCreateOptions) (cloudprovider.ICloudVpcPeeringConnection, error) { - return nil, cloudprovider.ErrNotImplemented -} -func (vpc *SVpc) AcceptICloudVpcPeeringConnection(id string) error { - return cloudprovider.ErrNotImplemented +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 (vpc *SVpc) GetAuthorityOwnerId() string { - return "" +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 (vpc *SRegion) DeleteVpc(vpcId string) error { - return cloudprovider.ErrNotImplemented +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 index 4ae1b4eb4..2e95fc4e3 100644 --- a/pkg/multicloud/baidu/wire.go +++ b/pkg/multicloud/baidu/wire.go @@ -13,3 +13,86 @@ // 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 index e72b2de33..852440388 100644 --- a/pkg/multicloud/baidu/zone.go +++ b/pkg/multicloud/baidu/zone.go @@ -15,22 +15,20 @@ 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 - host *SHost - - iwires []cloudprovider.ICloudWire - istorages []cloudprovider.ICloudStorage ZoneName string `json:"zoneName"` - - /* 支持的磁盘种类集合 */ - storageTypes []string } func (zone *SZone) GetId() string { @@ -38,9 +36,106 @@ func (zone *SZone) GetId() string { } func (zone *SZone) GetGlobalId() string { - return zone.ZoneName + 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 +}