diff --git a/pkg/multicloud/huawei/client/client.go b/pkg/multicloud/huawei/client/client.go index 2fcce885db..e311a3b376 100644 --- a/pkg/multicloud/huawei/client/client.go +++ b/pkg/multicloud/huawei/client/client.go @@ -28,11 +28,9 @@ type Client struct { init bool Balances *modules.SBalanceManager - Bandwidths *modules.SBandwidthManager Credentials *modules.SCredentialManager Disks *modules.SDiskManager Domains *modules.SDomainManager - Eips *modules.SEipManager Elasticcache *modules.SElasticcacheManager DcsAvailableZone *modules.SDcsAvailableZoneManager Flavors *modules.SFlavorManager @@ -125,7 +123,6 @@ func (self *Client) SetHttpClient(httpClient *http.Client) { self.Regions.SetHttpClient(httpClient) self.Zones.SetHttpClient(httpClient) self.Vpcs.SetHttpClient(httpClient) - self.Eips.SetHttpClient(httpClient) self.Elasticcache.SetHttpClient(httpClient) self.DcsAvailableZone.SetHttpClient(httpClient) self.Disks.SetHttpClient(httpClient) @@ -140,7 +137,6 @@ func (self *Client) SetHttpClient(httpClient *http.Client) { self.Interface.SetHttpClient(httpClient) self.Jobs.SetHttpClient(httpClient) self.Balances.SetHttpClient(httpClient) - self.Bandwidths.SetHttpClient(httpClient) self.Port.SetHttpClient(httpClient) self.Flavors.SetHttpClient(httpClient) self.VpcRoutes.SetHttpClient(httpClient) @@ -201,7 +197,6 @@ func (self *Client) initManagers() { self.Regions = modules.NewRegionManager(self.cfg) self.Zones = modules.NewZoneManager(self.cfg) self.Vpcs = modules.NewVpcManager(self.cfg) - self.Eips = modules.NewEipManager(self.cfg) self.Elasticcache = modules.NewElasticcacheManager(self.cfg) self.DcsAvailableZone = modules.NewDcsAvailableZoneManager(self.cfg) self.Disks = modules.NewDiskManager(self.cfg) @@ -216,7 +211,6 @@ func (self *Client) initManagers() { self.Interface = modules.NewInterfaceManager(self.cfg) self.Jobs = modules.NewJobManager(self.cfg) self.Balances = modules.NewBalanceManager(self.cfg) - self.Bandwidths = modules.NewBandwidthManager(self.cfg) self.Credentials = modules.NewCredentialManager(self.cfg) self.Port = modules.NewPortManager(self.cfg) self.Flavors = modules.NewFlavorManager(self.cfg) diff --git a/pkg/multicloud/huawei/client/modules/mod_bandwidths.go b/pkg/multicloud/huawei/client/modules/mod_bandwidths.go deleted file mode 100644 index 63a7f24a9e..0000000000 --- a/pkg/multicloud/huawei/client/modules/mod_bandwidths.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2019 Yunion -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package modules - -import ( - "yunion.io/x/onecloud/pkg/multicloud/huawei/client/manager" -) - -type SBandwidthManager struct { - SResourceManager -} - -func NewBandwidthManager(cfg manager.IManagerConfig) *SBandwidthManager { - return &SBandwidthManager{SResourceManager: SResourceManager{ - SBaseManager: NewBaseManager(cfg), - ServiceName: ServiceNameVPC, - Region: cfg.GetRegionId(), - ProjectId: cfg.GetProjectId(), - version: "v1", - Keyword: "bandwidth", - KeywordPlural: "bandwidths", - - ResourceKeyword: "bandwidths", - }} -} diff --git a/pkg/multicloud/huawei/client/modules/mod_eips.go b/pkg/multicloud/huawei/client/modules/mod_eips.go deleted file mode 100644 index 36bcf56387..0000000000 --- a/pkg/multicloud/huawei/client/modules/mod_eips.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2019 Yunion -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package modules - -import ( - "yunion.io/x/jsonutils" - - "yunion.io/x/onecloud/pkg/multicloud/huawei/client/manager" -) - -type SEipManager struct { - SResourceManager -} - -func NewEipManager(cfg manager.IManagerConfig) *SEipManager { - return &SEipManager{SResourceManager: SResourceManager{ - SBaseManager: NewBaseManager(cfg), - ServiceName: ServiceNameVPC, - Region: cfg.GetRegionId(), - ProjectId: cfg.GetProjectId(), - version: "v1", - Keyword: "publicip", - KeywordPlural: "publicips", - - ResourceKeyword: "publicips", - }} -} - -// https://support.huaweicloud.com/api-eip/eip_api_0005.html -func (self *SEipManager) Delete(id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) { - return self.DeleteInContextWithSpec(self.ctx, id, "", nil, params, "") -} diff --git a/pkg/multicloud/huawei/eip.go b/pkg/multicloud/huawei/eip.go index 1bffafb16c..980014bd85 100644 --- a/pkg/multicloud/huawei/eip.go +++ b/pkg/multicloud/huawei/eip.go @@ -16,11 +16,11 @@ package huawei import ( "fmt" + "net/url" "time" "yunion.io/x/jsonutils" "yunion.io/x/log" - "yunion.io/x/pkg/errors" billing_api "yunion.io/x/onecloud/pkg/apis/billing" api "yunion.io/x/onecloud/pkg/apis/compute" @@ -69,6 +69,7 @@ type SEipAddress struct { multicloud.SEipBase multicloud.HuaweiTags + Alias string ID string `json:"id"` Status string `json:"status"` Profile *SProfile `json:"profile,omitempty"` @@ -92,10 +93,9 @@ func (self *SEipAddress) GetId() string { } func (self *SEipAddress) GetName() string { - if len(self.BandwidthName) == 0 { - return self.BandwidthName + if len(self.Alias) > 0 { + return self.Alias } - return self.PublicIPAddress } @@ -208,20 +208,17 @@ func (self *SEipAddress) GetInternetChargeType() string { if err != nil { return api.EIP_CHARGE_TYPE_BY_TRAFFIC } - - if bandwidth.ChargeMode != "traffic" { - return api.EIP_CHARGE_TYPE_BY_BANDWIDTH - } else { + if bandwidth.ChargeMode == "traffic" { return api.EIP_CHARGE_TYPE_BY_TRAFFIC } + return api.EIP_CHARGE_TYPE_BY_BANDWIDTH } func (self *SEipAddress) GetBillingType() string { if self.Profile == nil { return billing_api.BILLING_TYPE_POSTPAID - } else { - return billing_api.BILLING_TYPE_PREPAID } + return billing_api.BILLING_TYPE_PREPAID } func (self *SEipAddress) GetCreatedAt() time.Time { @@ -260,20 +257,11 @@ func (self *SEipAddress) Associate(conf *cloudprovider.AssociateConfig) error { } func (self *SEipAddress) Dissociate() error { - if len(self.PortId) == 0 { - return nil - } - port, err := self.region.GetPort(self.PortId) + err := self.region.DissociateEip(self.ID) if err != nil { - return errors.Wrapf(err, "GetPort(%s)", self.PortId) + return err } - - err = self.region.DissociateEip(self.ID, port.DeviceID) - if err != nil { - return errors.Wrapf(err, "DissociateEip") - } - err = cloudprovider.WaitStatus(self, api.EIP_STATUS_READY, 10*time.Second, 180*time.Second) - return err + return cloudprovider.WaitStatus(self, api.EIP_STATUS_READY, 10*time.Second, 180*time.Second) } func (self *SEipAddress) ChangeBandwidth(bw int) error { @@ -297,43 +285,41 @@ func (self *SRegion) GetInstancePortId(instanceId string) (string, error) { // https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090596.html func (self *SRegion) AllocateEIP(name string, bwMbps int, chargeType TInternetChargeType, bgpType string, projectId string) (*SEipAddress, error) { - paramsStr := ` -{ - "publicip": { - "type": "%s", - "ip_version": 4 - }, - "bandwidth": { - "name": "%s", - "size": %d, - "share_type": "PER", - "charge_mode": "%s" - } -} -` - if len(bgpType) == 0 { - return nil, fmt.Errorf("AllocateEIP bgp type should not be empty") + params := map[string]interface{}{ + "bandwidth": map[string]interface{}{ + "name": name, + "size": bwMbps, + "share_type": "PER", + "charge_mode": chargeType, + }, + "publicip": map[string]interface{}{ + "type": bgpType, + "ip_version": 4, + "alias": name, + }, } - paramsStr = fmt.Sprintf(paramsStr, bgpType, name, bwMbps, chargeType) - _params, _ := jsonutils.ParseString(paramsStr) - params := _params.(*jsonutils.JSONDict) if len(projectId) > 0 { - params.Set("enterprise_project_id", jsonutils.NewString(projectId)) + params["enterprise_project_id"] = projectId } - eip := SEipAddress{} - err := DoCreate(self.ecsClient.Eips.Create, params, &eip) - return &eip, err + resp, err := self.vpcCreate("publicips", params) + if err != nil { + return nil, err + } + eip := &SEipAddress{region: self} + return eip, resp.Unmarshal(eip, "publicip") } func (self *SRegion) GetEip(eipId string) (*SEipAddress, error) { - var eip SEipAddress - err := DoGet(self.ecsClient.Eips.Get, eipId, nil, &eip) - eip.region = self - return &eip, err + resp, err := self.vpcGet("publicips/" + eipId) + if err != nil { + return nil, err + } + eip := &SEipAddress{region: self} + return eip, resp.Unmarshal(eip, "publicip") } func (self *SRegion) DeallocateEIP(eipId string) error { - _, err := self.ecsClient.Eips.Delete(eipId, nil) + _, err := self.vpcDelete("publicips/" + eipId) return err } @@ -346,57 +332,61 @@ func (self *SRegion) AssociateEip(eipId string, instanceId string) error { } func (self *SRegion) AssociateEipWithPortId(eipId string, portId string) error { - params := jsonutils.NewDict() - publicIPObj := jsonutils.NewDict() - publicIPObj.Add(jsonutils.NewString(portId), "port_id") - params.Add(publicIPObj, "publicip") - - _, err := self.ecsClient.Eips.Update(eipId, params) + params := map[string]interface{}{ + "publicip": map[string]interface{}{ + "port_id": portId, + }, + } + _, err := self.vpcUpdate("publicips/"+eipId, params) return err } -func (self *SRegion) DissociateEip(eipId string, instanceId string) error { - eip, err := self.GetEip(eipId) - if err != nil { - return err - } - - // 已经是解绑状态 - if eip.Status == "DOWN" { - return nil - } - - remoteInstanceId := eip.GetAssociationExternalId() - if remoteInstanceId != instanceId { - return fmt.Errorf("eip %s associate with another instance %s", eipId, remoteInstanceId) - } - - paramsStr := `{"publicip":{"port_id":null}}` - params, _ := jsonutils.ParseString(paramsStr) - _, err = self.ecsClient.Eips.Update(eipId, params) - return err +func (self *SRegion) DissociateEip(eipId string) error { + return self.AssociateEipWithPortId(eipId, "") } func (self *SRegion) UpdateEipBandwidth(bandwidthId string, bw int) error { - paramStr := `{ - "bandwidth": - { - "size": %d - } - }` - - paramStr = fmt.Sprintf(paramStr, bw) - params, _ := jsonutils.ParseString(paramStr) - _, err := self.ecsClient.Bandwidths.Update(bandwidthId, params) + params := map[string]interface{}{ + "bandwidth": map[string]interface{}{ + "size": bw, + }, + } + _, err := self.vpcUpdate("bandwidths/"+bandwidthId, params) return err } -func (self *SRegion) GetEipBandwidth(bandwidthId string) (Bandwidth, error) { - bandwidth := Bandwidth{} - err := DoGet(self.ecsClient.Bandwidths.Get, bandwidthId, nil, &bandwidth) - return bandwidth, err +func (self *SRegion) GetEipBandwidth(id string) (*Bandwidth, error) { + resp, err := self.vpcGet("bandwidths/" + id) + if err != nil { + return nil, err + } + ret := &Bandwidth{} + return ret, resp.Unmarshal(ret, "bandwidth") } func (self *SEipAddress) GetProjectId() string { return self.EnterpriseProjectId } + +func (self *SRegion) GetEips(portId string, addrs []string) ([]SEipAddress, error) { + query := url.Values{} + for _, addr := range addrs { + query.Add("public_ip_address", addr) + } + if len(portId) > 0 { + query.Set("port_id", portId) + } + resp, err := self.vpcList("publicips", query) + if err != nil { + return nil, err + } + eips := []SEipAddress{} + err = resp.Unmarshal(&eips, "publicips") + if err != nil { + return nil, err + } + for i := range eips { + eips[i].region = self + } + return eips, nil +} diff --git a/pkg/multicloud/huawei/huawei.go b/pkg/multicloud/huawei/huawei.go index fdeec6d9bf..966e681ba4 100644 --- a/pkg/multicloud/huawei/huawei.go +++ b/pkg/multicloud/huawei/huawei.go @@ -223,12 +223,12 @@ func (self *SHuaweiClient) lbGet(regionId, resource string) (jsonutils.JSONObjec func (self *SHuaweiClient) lbCreate(regionId, resource string, params map[string]interface{}) (jsonutils.JSONObject, error) { uri := fmt.Sprintf("https://elb.%s.myhuaweicloud.com/v2/%s/%s", regionId, self.projectId, resource) - return self.request(httputils.DELETE, uri, url.Values{}, params) + return self.request(httputils.POST, uri, url.Values{}, params) } -func (self *SHuaweiClient) lbUpdate(regionId, resource string) (jsonutils.JSONObject, error) { +func (self *SHuaweiClient) lbUpdate(regionId, resource string, params map[string]interface{}) (jsonutils.JSONObject, error) { uri := fmt.Sprintf("https://elb.%s.myhuaweicloud.com/v2/%s/%s", regionId, self.projectId, resource) - return self.request(httputils.PUT, uri, url.Values{}, nil) + return self.request(httputils.PUT, uri, url.Values{}, params) } func (self *SHuaweiClient) lbDelete(regionId, resource string) (jsonutils.JSONObject, error) { @@ -236,6 +236,31 @@ func (self *SHuaweiClient) lbDelete(regionId, resource string) (jsonutils.JSONOb return self.request(httputils.DELETE, uri, url.Values{}, nil) } +func (self *SHuaweiClient) vpcList(regionId, resource string, query url.Values) (jsonutils.JSONObject, error) { + url := fmt.Sprintf("https://vpc.%s.myhuaweicloud.com/v1/%s/%s", regionId, self.projectId, resource) + return self.request(httputils.GET, url, query, nil) +} + +func (self *SHuaweiClient) vpcCreate(regionId, resource string, params map[string]interface{}) (jsonutils.JSONObject, error) { + uri := fmt.Sprintf("https://vpc.%s.myhuaweicloud.com/v1/%s/%s", regionId, self.projectId, resource) + return self.request(httputils.POST, uri, url.Values{}, params) +} + +func (self *SHuaweiClient) vpcGet(regionId, resource string) (jsonutils.JSONObject, error) { + uri := fmt.Sprintf("https://vpc.%s.myhuaweicloud.com/v1/%s/%s", regionId, self.projectId, resource) + return self.request(httputils.GET, uri, url.Values{}, nil) +} + +func (self *SHuaweiClient) vpcDelete(regionId, resource string) (jsonutils.JSONObject, error) { + uri := fmt.Sprintf("https://vpc.%s.myhuaweicloud.com/v1/%s/%s", regionId, self.projectId, resource) + return self.request(httputils.DELETE, uri, url.Values{}, nil) +} + +func (self *SHuaweiClient) vpcUpdate(regionId, resource string, params map[string]interface{}) (jsonutils.JSONObject, error) { + uri := fmt.Sprintf("https://vpc.%s.myhuaweicloud.com/v1/%s/%s", regionId, self.projectId, resource) + return self.request(httputils.PUT, uri, url.Values{}, params) +} + type akClient struct { client *http.Client aksk aksk.SignOptions @@ -243,7 +268,7 @@ type akClient struct { func (self *akClient) Do(req *http.Request) (*http.Response, error) { req.Header.Del("Accept") - if req.Method == string(httputils.GET) { + if req.Method == string(httputils.GET) || req.Method == string(httputils.DELETE) { req.Header.Del("Content-Length") } aksk.Sign(req, self.aksk) diff --git a/pkg/multicloud/huawei/instance.go b/pkg/multicloud/huawei/instance.go index badb4d18dc..e85e3052c5 100644 --- a/pkg/multicloud/huawei/instance.go +++ b/pkg/multicloud/huawei/instance.go @@ -430,17 +430,13 @@ func (self *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) { return nil, nil } - eips, err := self.host.zone.region.GetEips() + eips, err := self.host.zone.region.GetEips("", ips) if err != nil { return nil, err } - - for _, eip := range eips { - if eip.PublicIPAddress == ips[0] { - return &eip, nil - } + if len(eips) > 0 { + return &eips[0], nil } - return nil, nil } diff --git a/pkg/multicloud/huawei/loadbalancer.go b/pkg/multicloud/huawei/loadbalancer.go index 4ba5e3451a..1e382af968 100644 --- a/pkg/multicloud/huawei/loadbalancer.go +++ b/pkg/multicloud/huawei/loadbalancer.go @@ -176,15 +176,11 @@ func (self *SLoadbalancer) GetNetwork() *SNetwork { func (self *SLoadbalancer) GetEip() *SEipAddress { if self.eip == nil { - eips, _ := self.region.GetEips() + eips, _ := self.region.GetEips(self.VipPortID, nil) for i := range eips { - eip := &eips[i] - if eip.PortId == self.VipPortID { - self.eip = eip - } + self.eip = &eips[i] } } - return self.eip } @@ -536,6 +532,26 @@ func (self *SRegion) lbList(resource string, query url.Values) (jsonutils.JSONOb return self.client.lbList(self.ID, resource, query) } +func (self *SRegion) vpcList(resource string, query url.Values) (jsonutils.JSONObject, error) { + return self.client.vpcList(self.ID, resource, query) +} + +func (self *SRegion) vpcCreate(resource string, params map[string]interface{}) (jsonutils.JSONObject, error) { + return self.client.vpcCreate(self.ID, resource, params) +} + +func (self *SRegion) vpcGet(resource string) (jsonutils.JSONObject, error) { + return self.client.vpcGet(self.ID, resource) +} + +func (self *SRegion) vpcDelete(resource string) (jsonutils.JSONObject, error) { + return self.client.vpcDelete(self.ID, resource) +} + +func (self *SRegion) vpcUpdate(resource string, params map[string]interface{}) (jsonutils.JSONObject, error) { + return self.client.vpcUpdate(self.ID, resource, params) +} + func (self *SRegion) lbListAll(resource string, query url.Values, respKey string, retVal interface{}) error { ret := jsonutils.NewArray() for { @@ -562,15 +578,15 @@ func (self *SRegion) lbGet(resource string) (jsonutils.JSONObject, error) { } func (self *SRegion) lbDelete(resource string) (jsonutils.JSONObject, error) { - return self.client.lbGet(self.ID, resource) + return self.client.lbDelete(self.ID, resource) } func (self *SRegion) lbCreate(resource string, params map[string]interface{}) (jsonutils.JSONObject, error) { - return self.client.lbGet(self.ID, resource) + return self.client.lbCreate(self.ID, resource, params) } func (self *SRegion) lbUpdate(resource string, params map[string]interface{}) (jsonutils.JSONObject, error) { - return self.client.lbUpdate(self.ID, resource) + return self.client.lbUpdate(self.ID, resource, params) } // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561535.html diff --git a/pkg/multicloud/huawei/region.go b/pkg/multicloud/huawei/region.go index f5a1887852..baffc8c785 100644 --- a/pkg/multicloud/huawei/region.go +++ b/pkg/multicloud/huawei/region.go @@ -340,41 +340,16 @@ func (self *SRegion) GetIVpcs() ([]cloudprovider.ICloudVpc, error) { return self.ivpcs, nil } -func (self *SRegion) GetEipById(eipId string) (SEipAddress, error) { - var eip SEipAddress - err := DoGet(self.ecsClient.Eips.Get, eipId, nil, &eip) - eip.region = self - return eip, err -} - -// 返回参数分别为eip 列表、列表长度、error。 -// https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090598.html -func (self *SRegion) GetEips() ([]SEipAddress, error) { - querys := make(map[string]string) - - eips := make([]SEipAddress, 0) - err := doListAllWithMarker(self.ecsClient.Eips.List, querys, &eips) - for i := range eips { - eips[i].region = self - } - return eips, err -} - func (self *SRegion) GetIEips() ([]cloudprovider.ICloudEIP, error) { - _, err := self.getECSClient() + eips, err := self.GetEips("", nil) if err != nil { return nil, err } - eips, err := self.GetEips() - if err != nil { - return nil, err - } - - ret := make([]cloudprovider.ICloudEIP, len(eips)) + ret := []cloudprovider.ICloudEIP{} for i := 0; i < len(eips); i += 1 { eips[i].region = self - ret[i] = &eips[i] + ret = append(ret, &eips[i]) } return ret, nil } @@ -406,8 +381,11 @@ func (self *SRegion) GetIZoneById(id string) (cloudprovider.ICloudZone, error) { } func (self *SRegion) GetIEipById(eipId string) (cloudprovider.ICloudEIP, error) { - eip, err := self.GetEipById(eipId) - return &eip, err + eip, err := self.GetEip(eipId) + if err != nil { + return nil, err + } + return eip, nil } // https://support.huaweicloud.com/api-vpc/zh-cn_topic_0060595555.html diff --git a/pkg/multicloud/huawei/shell/eip.go b/pkg/multicloud/huawei/shell/eip.go index 70451342e6..b9fb7bdabb 100644 --- a/pkg/multicloud/huawei/shell/eip.go +++ b/pkg/multicloud/huawei/shell/eip.go @@ -21,9 +21,11 @@ import ( func init() { type EipListOptions struct { + PortId string + Addrs []string } shellutils.R(&EipListOptions{}, "eip-list", "List eips", func(cli *huawei.SRegion, args *EipListOptions) error { - eips, e := cli.GetEips() + eips, e := cli.GetEips(args.PortId, args.Addrs) if e != nil { return e } @@ -32,13 +34,14 @@ func init() { }) type EipAllocateOptions struct { - NAME string `help:"eip name"` - BW int `help:"Bandwidth limit in Mbps"` - BGP string `help:"bgp type" choices:"5_telcom|5_union|5_bgp|5_sbgp"` - ProjectId string + NAME string `help:"eip name"` + BW int `help:"Bandwidth limit in Mbps"` + BGP string `help:"bgp type" choices:"5_telcom|5_union|5_bgp|5_sbgp"` + ChargeType string `help:"eip charge type" default:"traffic" choices:"traffic|bandwidth"` + ProjectId string } shellutils.R(&EipAllocateOptions{}, "eip-create", "Allocate an EIP", func(cli *huawei.SRegion, args *EipAllocateOptions) error { - eip, err := cli.AllocateEIP(args.NAME, args.BW, huawei.InternetChargeByTraffic, args.BGP, args.ProjectId) + eip, err := cli.AllocateEIP(args.NAME, args.BW, huawei.TInternetChargeType(args.ChargeType), args.BGP, args.ProjectId) if err != nil { return err } @@ -59,11 +62,14 @@ func init() { INSTANCE string `help:"Instance ID"` } shellutils.R(&EipAssociateOptions{}, "eip-associate", "Associate an EIP", func(cli *huawei.SRegion, args *EipAssociateOptions) error { - err := cli.AssociateEip(args.ID, args.INSTANCE) - return err + return cli.AssociateEip(args.ID, args.INSTANCE) }) - shellutils.R(&EipAssociateOptions{}, "eip-dissociate", "Dissociate an EIP", func(cli *huawei.SRegion, args *EipAssociateOptions) error { - err := cli.DissociateEip(args.ID, args.INSTANCE) - return err + + type EipDissociateOptions struct { + ID string `help:"EIP allocation ID"` + } + + shellutils.R(&EipDissociateOptions{}, "eip-dissociate", "Dissociate an EIP", func(cli *huawei.SRegion, args *EipDissociateOptions) error { + return cli.DissociateEip(args.ID) }) }