From bdfd29d9af511bd42167285abe41b4442e387d5b Mon Sep 17 00:00:00 2001 From: lixiaojun Date: Thu, 25 Oct 2018 14:37:54 +0800 Subject: [PATCH 1/4] update to ucloud-sdk-go v0.5.7 --- .../private/protocol/http/client.go | 3 + .../ucloud/ucloud-sdk-go/private/utils/doc.go | 4 + .../services/uhost/types_uhost_disk_query.go | 9 +- .../services/uhost/types_uhost_disk_set.go | 6 + .../uhost/types_uhost_instance_set.go | 6 +- .../services/uhost/types_uhost_ipset.go | 6 + .../services/ulb/allocate_backend_batch.go | 2 +- .../services/ulb/types_backend_set.go | 16 ++ .../services/unet/types_vipdetail_set.go | 3 + .../ucloud/ucloud-sdk-go/services/vpc/doc.go | 4 + .../ucloud-sdk-go/ucloud/auth/credential.go | 4 + .../ucloud/ucloud-sdk-go/ucloud/client.go | 3 + .../ucloud/error/client_error.go | 19 +- .../ucloud-sdk-go/ucloud/error/error.go | 3 + .../ucloud/helpers/waiter/error.go | 43 +++++ .../ucloud/helpers/waiter/waiter.go | 179 ++++++++++++++++++ .../ucloud/ucloud-sdk-go/ucloud/log/logger.go | 3 + .../ucloud-sdk-go/ucloud/request/common.go | 3 + .../ucloud-sdk-go/ucloud/response/common.go | 3 + .../ucloud-sdk-go/ucloud/version/version.go | 8 +- vendor/vendor.json | 92 ++++----- 21 files changed, 360 insertions(+), 59 deletions(-) create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/private/utils/doc.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/services/ulb/types_backend_set.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/doc.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/ucloud/helpers/waiter/error.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/ucloud/helpers/waiter/waiter.go diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/private/protocol/http/client.go b/vendor/github.com/ucloud/ucloud-sdk-go/private/protocol/http/client.go index 3f1c0188d7..0d3e05dbe9 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/private/protocol/http/client.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/private/protocol/http/client.go @@ -1,3 +1,6 @@ +/* +Package http is an implementation of http protocol +*/ package http import ( diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/private/utils/doc.go b/vendor/github.com/ucloud/ucloud-sdk-go/private/utils/doc.go new file mode 100644 index 0000000000..621a54c3c9 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/private/utils/doc.go @@ -0,0 +1,4 @@ +/* +Package utils is the utilities to process internal data of sdk +*/ +package utils diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/types_uhost_disk_query.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/types_uhost_disk_query.go index 1f183e5660..264e5aeaf3 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/types_uhost_disk_query.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/types_uhost_disk_query.go @@ -1,20 +1,17 @@ package uhost /* - UHostDisk - - - this model is auto created by ucloud code generater for open api, - you can also see https://docs.ucloud.cn for detail. + UHostDisk - the request query for disk of uhost */ type UHostDisk struct { // 磁盘大小,单位GB。 - Size *string `required:"true"` + Size *int `required:"true"` // 磁盘类型。枚举值:LOCAL_NORMAL 普通本地盘 | CLOUD_NORMAL 普通云盘 |LOCAL_SSD SSD本地盘 | CLOUD_SSD SSD云盘,默认为LOCAL_NORMAL。磁盘仅支持有限组合,详情请查询控制台。 Type *string `required:"true"` // 是否是系统盘。枚举值:True|False - IsBoot *bool `required:"true"` + IsBoot *string `required:"true"` // NONE|DATAARK BackupType *string `required:"false"` diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/types_uhost_disk_set.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/types_uhost_disk_set.go index c484a87097..7ec98645a5 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/types_uhost_disk_set.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/types_uhost_disk_set.go @@ -23,6 +23,12 @@ type UHostDiskSet struct { // 磁盘大小,单位: GB Size int + // 是否是系统盘 + IsBoot string + + // 磁盘类型 + DiskType string + // 备份类型,DataArk BackupType string } diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/types_uhost_instance_set.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/types_uhost_instance_set.go index aa92313fcf..c1360fd15a 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/types_uhost_instance_set.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/types_uhost_instance_set.go @@ -98,12 +98,12 @@ type UHostInstanceSet struct { // 主机的生命周期类型。目前仅支持Normal:普通; LifeCycle string - // 主机的 GPU 数量 + // GPU个数 GPU int - // 系统盘状态 Normal: 已初始化完成 + // 系统盘状态 Normal表示初始化完成;Initializing表示在初始化 BootDiskState string - // 主机的存储空间 + // 总的存储空间 TotalDiskSpace int } diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/types_uhost_ipset.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/types_uhost_ipset.go index 6608f6df19..ecbe6a8916 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/types_uhost_ipset.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/types_uhost_ipset.go @@ -19,4 +19,10 @@ type UHostIPSet struct { // IP对应的带宽, 单位: Mb (内网IP不显示带宽信息) Bandwidth int + + // VPC ID + VPCId string + + // Subnet Id + SubnetId string } diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/ulb/allocate_backend_batch.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/ulb/allocate_backend_batch.go index 9301474a4e..302d37e374 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/services/ulb/allocate_backend_batch.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/ulb/allocate_backend_batch.go @@ -30,7 +30,7 @@ type AllocateBackendBatchResponse struct { response.CommonBase // 所添加的后端资源ID,(为ULB系统中使用,与资源自身ID无关),可用于 UpdateBackendAttribute/UpdateBackendAttributeBatch/ReleaseBackend - BackendId string + BackendSet []BackendSet } // NewAllocateBackendBatchRequest will create request of AllocateBackendBatch action. diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/ulb/types_backend_set.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/ulb/types_backend_set.go new file mode 100644 index 0000000000..5008a2c81e --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/ulb/types_backend_set.go @@ -0,0 +1,16 @@ +package ulb + +/* + BackendSet - ulb添加rs时返回的信息 + + this model is auto created by ucloud code generater for open api, + you can also see https://docs.ucloud.cn for detail. +*/ +type BackendSet struct { + + // rs的资源ID + BackendId string + + // rs对应的UHost ID + ResourceId string +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/unet/types_vipdetail_set.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/unet/types_vipdetail_set.go index 66662e6d11..92fd860525 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/services/unet/types_vipdetail_set.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/unet/types_vipdetail_set.go @@ -28,4 +28,7 @@ type VIPDetailSet struct { // VPC id VPCId string + + // Virtual IP 名称 + Name string } diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/doc.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/doc.go new file mode 100644 index 0000000000..f107ed1d2f --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/doc.go @@ -0,0 +1,4 @@ +/* + Package vpc include resources of ucloud vpc 2.0 product +*/ +package vpc diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/auth/credential.go b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/auth/credential.go index 5d924e6848..05ff2409cc 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/auth/credential.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/auth/credential.go @@ -1,3 +1,6 @@ +/* +Package auth is the credential utilities of sdk +*/ package auth import ( @@ -9,6 +12,7 @@ import ( "strings" ) +// Credential is the information of credential keys type Credential struct { PublicKey string PrivateKey string diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/client.go b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/client.go index c1176763d0..6722a77af5 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/client.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/client.go @@ -1,3 +1,6 @@ +/* +Package ucloud is a package of utilities to setup ucloud sdk and improve using experience +*/ package ucloud import ( diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/error/client_error.go b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/error/client_error.go index f6f31bc1b8..c7679cd51b 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/error/client_error.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/error/client_error.go @@ -3,6 +3,7 @@ package uerr import ( "fmt" "net" + "strings" ) var ( @@ -37,7 +38,7 @@ func NewClientError(name string, err error) ClientError { return ClientError{ name: name, err: err, - retryable: false, + retryable: isRetryableName(name), } } @@ -51,7 +52,7 @@ func (e ClientError) Code() int { return -1 } -// HTTPStatusCode will return http status code +// StatusCode will return http status code func (e ClientError) StatusCode() int { return 0 } @@ -77,5 +78,17 @@ func IsNetworkError(err error) bool { return false } _, isNetError := err.(net.Error) - return isNetError + if isNetError { + return true + } + return strings.HasPrefix(err.Error(), "net/http: request canceled") +} + +func isRetryableName(name string) bool { + switch name { + case ErrNetwork: + return true + default: + return false + } } diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/error/error.go b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/error/error.go index 9810c3dc55..e5ea08e3d8 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/error/error.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/error/error.go @@ -1,3 +1,6 @@ +/* +Package uerr is the error definition of service and sdk +*/ package uerr // Error is the ucloud sdk error diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/helpers/waiter/error.go b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/helpers/waiter/error.go new file mode 100644 index 0000000000..de57966ab1 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/helpers/waiter/error.go @@ -0,0 +1,43 @@ +package waiter + +import ( + "fmt" + "strings" + "time" + + "github.com/pkg/errors" +) + +var ( + errTimeoutConf = errors.New("timeout cannot be set zero") +) + +// TimeoutError is returned when WaitForState times out +type TimeoutError struct { + LastError error + LastState string + Timeout time.Duration + ExpectedStates []string +} + +func (e *TimeoutError) Error() string { + errors := []string{"cannot waiting for resource is completed"} + + if e.Timeout > 0 { + errors = append(errors, fmt.Sprintf("timeout: %s", e.Timeout)) + } + + if e.LastState != "" { + errors = append(errors, fmt.Sprintf("last state: %q", e.LastState)) + } + + if len(e.ExpectedStates) > 0 { + errors = append(errors, fmt.Sprintf("want: %q", strings.Join(e.ExpectedStates, ","))) + } + + if e.LastError != nil { + errors = append(errors, fmt.Sprintf("err: %s", e.LastError)) + } + + return strings.Join(errors, ", ") +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/helpers/waiter/waiter.go b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/helpers/waiter/waiter.go new file mode 100644 index 0000000000..1018a8be7e --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/helpers/waiter/waiter.go @@ -0,0 +1,179 @@ +/* +Package waiter is a helper package use for waiting remote state is transformed into target state. +*/ +package waiter + +import ( + "time" + + "github.com/ucloud/ucloud-sdk-go/ucloud/log" +) + +const graceRefreshTimeout = 30 * time.Second + +// RefreshFunc is the function to query remote resource state +type RefreshFunc func() (result interface{}, state string, err error) + +// StateWaiter is the waiter that waiting for remote state achieve to target state +type StateWaiter struct { + Pending []string + Target []string + Refresh RefreshFunc + Delay time.Duration + Timeout time.Duration + MinTimeout time.Duration + PollInterval time.Duration +} + +// Wait watches an object and waits for it to achieve the state +func (waiter *StateWaiter) Wait() (interface{}, error) { + if waiter.Timeout == 0 { + return nil, errTimeoutConf + } + + // it is a re-implementation of terraform StateChangeConf + log.Debugf("waiting for state: %s", waiter.Target) + + resCh := make(chan refreshResult, 1) + cancelCh := make(chan struct{}) + + result := refreshResult{} + + go func() { + defer close(resCh) + + // delay before refresh + time.Sleep(waiter.Delay) + + var wait time.Duration + + for { + // send an empty result to active channel + resCh <- result + + select { + case <-cancelCh: + return + case <-time.After(wait): + // first round should not wait + // initial wait with 100ms + if wait == 0 { + wait = 100 * time.Millisecond + } + } + + // refresh newest state + res, currentState, err := waiter.Refresh() + result = refreshResult{ + Result: res, + State: currentState, + Error: err, + } + + if err != nil { + resCh <- result + return + } + + // if target state is achieved, done + for _, allowed := range waiter.Target { + if currentState == allowed { + result.Done = true + resCh <- result + return + } + } + + isPending := false + for _, allowed := range waiter.Pending { + if currentState == allowed { + isPending = true + break + } + } + + if !isPending && len(waiter.Pending) > 0 { + resCh <- result + return + } + + // wait interval using exponential backoff, policy as follow: + // + // * 0 100ms 200ms 400ms 800ms 1.6s 3.2s 6.4s 10s 10s ... (0 <= MinTimeout <= 100ms) + // * 0 3s 6s 10s 10s ... (100ms < MinTimeout <= 10s, eg. 3s) + // * 0 11s 10s 10s 10s ... (10s < MinTimeout, eg. 11s) + wait *= 2 + + if waiter.PollInterval > 0 && waiter.PollInterval < 180*time.Second { + wait = waiter.PollInterval + } else { + if wait < waiter.MinTimeout { + wait = waiter.MinTimeout + } else if wait > 10*time.Second { + wait = 10 * time.Second + } + } + } + }() + + // store the last value result from the refresh loop + var lastRes refreshResult + + timeout := time.After(waiter.Timeout) + for { + select { + case r, ok := <-resCh: + if !ok { + return lastRes.Result, lastRes.Error + } + + if r.Done { + return r.Result, r.Error + } + + lastRes = r + case <-timeout: + log.Debugf("wait for state timeout after %s", waiter.Timeout) + + // cancel the goroutine and start our grace period timer + close(cancelCh) + + return waiter.waitForTimeout(resCh, lastRes) + } + } +} + +type refreshResult struct { + Result interface{} + State string + Error error + Done bool +} + +func (waiter *StateWaiter) waitForTimeout(resCh <-chan refreshResult, last refreshResult) (interface{}, error) { + timeout := time.After(graceRefreshTimeout) + + errTimeout := &TimeoutError{ + LastError: last.Error, + LastState: last.State, + Timeout: waiter.Timeout, + ExpectedStates: waiter.Target, + } + + // we need wait until at lease once refresh is completed, + // and close the cancel channel + select { + case r, ok := <-resCh: + if r.Done { + return r.Result, r.Error + } + + if !ok { + return nil, errTimeout + } + case <-timeout: + return nil, errTimeout + } + + return nil, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/log/logger.go b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/log/logger.go index 7633e203a2..7834c9e6cc 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/log/logger.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/log/logger.go @@ -1,3 +1,6 @@ +/* +Package log is the log utilities of sdk +*/ package log import ( diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/request/common.go b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/request/common.go index daf8d7a267..c2fbbf6657 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/request/common.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/request/common.go @@ -1,3 +1,6 @@ +/* +Package request is the request of service +*/ package request import "time" diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/response/common.go b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/response/common.go index 15a85dddc3..a192fe9f08 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/response/common.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/response/common.go @@ -1,3 +1,6 @@ +/* +Package response is the response of service +*/ package response // Common describe a response of action, diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/version/version.go b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/version/version.go index b4303a7a38..130c325d35 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/version/version.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/version/version.go @@ -1,5 +1,7 @@ +/* +Package version is the version of sdk +*/ package version -// Version is the version of sdk -// See also semantic version: https://semver.org/ -const Version = "0.5.3" +// Version see also semantic version: https://semver.org/ +const Version = "0.5.7" diff --git a/vendor/vendor.json b/vendor/vendor.json index 4862f73c9b..61a34bc176 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -33,94 +33,100 @@ "revisionTime": "2018-06-01T13:25:42Z" }, { - "checksumSHA1": "ce1ojjUrhPY/xhT+4TnHOXN/CRY=", + "checksumSHA1": "CM3d5OmqPG8/5JSZ4iDintmOxPY=", "path": "github.com/ucloud/ucloud-sdk-go/private/protocol/http", - "revision": "ec0776932e60fda35bd18ec266b5e081777d1dea", - "revisionTime": "2018-10-15T11:12:01Z" + "revision": "ae92fcdf66a5f34c27d8ae1f81b10033648275f4", + "revisionTime": "2018-10-24T10:31:01Z" }, { - "checksumSHA1": "Y3QF1O0Qwwap4lrWmJRNPmmK/gM=", + "checksumSHA1": "cXu7QmFV8l7353qEiDeYyi4r54g=", "path": "github.com/ucloud/ucloud-sdk-go/private/utils", - "revision": "ec0776932e60fda35bd18ec266b5e081777d1dea", - "revisionTime": "2018-10-15T11:12:01Z" + "revision": "ae92fcdf66a5f34c27d8ae1f81b10033648275f4", + "revisionTime": "2018-10-24T10:31:01Z" }, { "checksumSHA1": "yMCT15wmLZ0Jtf8w0qgc8fGbS8A=", "path": "github.com/ucloud/ucloud-sdk-go/services/pathx", - "revision": "ec0776932e60fda35bd18ec266b5e081777d1dea", - "revisionTime": "2018-10-15T11:12:01Z" + "revision": "ae92fcdf66a5f34c27d8ae1f81b10033648275f4", + "revisionTime": "2018-10-24T10:31:01Z" }, { "checksumSHA1": "DtQemSHmV3oVhUgqes+4rTSQ0bU=", "path": "github.com/ucloud/ucloud-sdk-go/services/uaccount", - "revision": "ec0776932e60fda35bd18ec266b5e081777d1dea", - "revisionTime": "2018-10-15T11:12:01Z" + "revision": "ae92fcdf66a5f34c27d8ae1f81b10033648275f4", + "revisionTime": "2018-10-24T10:31:01Z" }, { - "checksumSHA1": "rc/SBGZkIt220mDrE1IXRW+wzAc=", + "checksumSHA1": "mT4ix7tzri+ORhkQDUvNvRuGK4Y=", "path": "github.com/ucloud/ucloud-sdk-go/services/uhost", - "revision": "ec0776932e60fda35bd18ec266b5e081777d1dea", - "revisionTime": "2018-10-15T11:12:01Z" + "revision": "ae92fcdf66a5f34c27d8ae1f81b10033648275f4", + "revisionTime": "2018-10-24T10:31:01Z" }, { - "checksumSHA1": "WD0uUydaarKlATUzC6gfWqtLSAM=", + "checksumSHA1": "xRS1wB0vZYNXb32v1gUZOjxULa8=", "path": "github.com/ucloud/ucloud-sdk-go/services/ulb", - "revision": "ec0776932e60fda35bd18ec266b5e081777d1dea", - "revisionTime": "2018-10-15T11:12:01Z" + "revision": "ae92fcdf66a5f34c27d8ae1f81b10033648275f4", + "revisionTime": "2018-10-24T10:31:01Z" }, { - "checksumSHA1": "BD65R7aEd+H/PrzWCvMeAAI/jOE=", + "checksumSHA1": "QbXRY5SSHJ7fMidexqgzQvseEBE=", "path": "github.com/ucloud/ucloud-sdk-go/services/unet", - "revision": "ec0776932e60fda35bd18ec266b5e081777d1dea", - "revisionTime": "2018-10-15T11:12:01Z" + "revision": "ae92fcdf66a5f34c27d8ae1f81b10033648275f4", + "revisionTime": "2018-10-24T10:31:01Z" }, { - "checksumSHA1": "N/rfCFkvkqn9TPG5ZiuBqvkJRpc=", + "checksumSHA1": "LnJNShxWW+DLunVWzgfaGa2+lSY=", "path": "github.com/ucloud/ucloud-sdk-go/services/vpc", - "revision": "ec0776932e60fda35bd18ec266b5e081777d1dea", - "revisionTime": "2018-10-15T11:12:01Z" + "revision": "ae92fcdf66a5f34c27d8ae1f81b10033648275f4", + "revisionTime": "2018-10-24T10:31:01Z" }, { - "checksumSHA1": "q4HTIi6/8WDgS4m8qr6lTLm9acU=", + "checksumSHA1": "UdftGOVglTitYFfgIsnk+MKu9x4=", "path": "github.com/ucloud/ucloud-sdk-go/ucloud", - "revision": "ec0776932e60fda35bd18ec266b5e081777d1dea", - "revisionTime": "2018-10-15T11:12:01Z" + "revision": "ae92fcdf66a5f34c27d8ae1f81b10033648275f4", + "revisionTime": "2018-10-24T10:31:01Z" }, { - "checksumSHA1": "sUq3usdKdnNuhRNxEcrXvnclPhQ=", + "checksumSHA1": "SYdYJOqUQHfcEMAOSQok5aQ+xGU=", "path": "github.com/ucloud/ucloud-sdk-go/ucloud/auth", - "revision": "ec0776932e60fda35bd18ec266b5e081777d1dea", - "revisionTime": "2018-10-15T11:12:01Z" + "revision": "ae92fcdf66a5f34c27d8ae1f81b10033648275f4", + "revisionTime": "2018-10-24T10:31:01Z" }, { - "checksumSHA1": "LSBXk2M0EQ8mOcpEIW2tYNW3NIo=", + "checksumSHA1": "RqE8dhOl1gwfHj5e9X09VDa9VE8=", "path": "github.com/ucloud/ucloud-sdk-go/ucloud/error", - "revision": "ec0776932e60fda35bd18ec266b5e081777d1dea", - "revisionTime": "2018-10-15T11:12:01Z" + "revision": "ae92fcdf66a5f34c27d8ae1f81b10033648275f4", + "revisionTime": "2018-10-24T10:31:01Z" }, { - "checksumSHA1": "a6tTIq01vKr4hQqYfZf/e3o8M+A=", + "checksumSHA1": "WMTXD2ama/EmXjmBbAheUSie7P4=", + "path": "github.com/ucloud/ucloud-sdk-go/ucloud/helpers/waiter", + "revision": "ae92fcdf66a5f34c27d8ae1f81b10033648275f4", + "revisionTime": "2018-10-24T10:31:01Z" + }, + { + "checksumSHA1": "ExftuufZDUOoEt4LlBX/GVYz3W4=", "path": "github.com/ucloud/ucloud-sdk-go/ucloud/log", - "revision": "ec0776932e60fda35bd18ec266b5e081777d1dea", - "revisionTime": "2018-10-15T11:12:01Z" + "revision": "ae92fcdf66a5f34c27d8ae1f81b10033648275f4", + "revisionTime": "2018-10-24T10:31:01Z" }, { - "checksumSHA1": "1/fi2w6QIq6ilVolTpAQjekK9bw=", + "checksumSHA1": "vJhTZT1X5lIdwqmZEWekIGs8tbw=", "path": "github.com/ucloud/ucloud-sdk-go/ucloud/request", - "revision": "ec0776932e60fda35bd18ec266b5e081777d1dea", - "revisionTime": "2018-10-15T11:12:01Z" + "revision": "ae92fcdf66a5f34c27d8ae1f81b10033648275f4", + "revisionTime": "2018-10-24T10:31:01Z" }, { - "checksumSHA1": "3/Pw64ads2WyO+Y+RjRLVQd0hCY=", + "checksumSHA1": "UYVTJ2g0IVPxfhSUS0l0/RHyrGg=", "path": "github.com/ucloud/ucloud-sdk-go/ucloud/response", - "revision": "ec0776932e60fda35bd18ec266b5e081777d1dea", - "revisionTime": "2018-10-15T11:12:01Z" + "revision": "ae92fcdf66a5f34c27d8ae1f81b10033648275f4", + "revisionTime": "2018-10-24T10:31:01Z" }, { - "checksumSHA1": "t7/DYvWlOqwYvCDlzMMSu6JazCE=", + "checksumSHA1": "DYRmPB3XGrNFOa0zhPDnHxge4gE=", "path": "github.com/ucloud/ucloud-sdk-go/ucloud/version", - "revision": "ec0776932e60fda35bd18ec266b5e081777d1dea", - "revisionTime": "2018-10-15T11:12:01Z" + "revision": "ae92fcdf66a5f34c27d8ae1f81b10033648275f4", + "revisionTime": "2018-10-24T10:31:01Z" }, { "checksumSHA1": "BGm8lKZmvJbf/YOJLeL1rw2WVjA=", From f8a4fef56a294815b31dd99e9db63b6922f68f45 Mon Sep 17 00:00:00 2001 From: lixiaojun Date: Wed, 31 Oct 2018 20:45:21 +0800 Subject: [PATCH 2/4] add disk --- README.md | 3 +- base/client.go | 3 + base/util.go | 58 +++ cmd/configure.go | 2 +- cmd/disk.go | 427 ++++++++++++++++++ cmd/eip.go | 58 ++- cmd/globalssh.go | 13 +- cmd/root.go | 4 +- cmd/uhost.go | 297 ++++++------ cmd/unet.go | 22 +- model/status/status.go | 9 + ux/prompt.go | 3 + vendor/github.com/spf13/cobra/command.go | 1 + .../services/udisk/attach_udisk.go | 59 +++ .../ucloud-sdk-go/services/udisk/client.go | 19 + .../services/udisk/clone_udisk.go | 71 +++ .../services/udisk/clone_udisk_snapshot.go | 69 +++ .../services/udisk/create_udisk.go | 74 +++ .../services/udisk/create_udisk_snapshot.go | 60 +++ .../services/udisk/delete_udisk.go | 50 ++ .../services/udisk/describe_udisk.go | 65 +++ .../services/udisk/describe_udisk_price.go | 65 +++ .../udisk/describe_udisk_upgrade_price.go | 62 +++ .../services/udisk/detach_udisk.go | 59 +++ .../ucloud-sdk-go/services/udisk/doc.go | 11 + .../ucloud-sdk-go/services/udisk/enums.go | 1 + .../services/udisk/rename_udisk.go | 53 +++ .../services/udisk/resize_udisk.go | 56 +++ .../services/udisk/restore_udisk.go | 56 +++ .../udisk/set_udisk_udata_ark_mode.go | 53 +++ .../services/udisk/types_udisk_data_set.go | 64 +++ .../udisk/types_udisk_price_data_set.go | 19 + vendor/vendor.json | 6 + 33 files changed, 1694 insertions(+), 178 deletions(-) create mode 100644 cmd/disk.go create mode 100644 model/status/status.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/attach_udisk.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/client.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/clone_udisk.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/clone_udisk_snapshot.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/create_udisk.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/create_udisk_snapshot.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/delete_udisk.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/describe_udisk.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/describe_udisk_price.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/describe_udisk_upgrade_price.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/detach_udisk.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/doc.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/enums.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/rename_udisk.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/resize_udisk.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/restore_udisk.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/set_udisk_udata_ark_mode.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/types_udisk_data_set.go create mode 100644 vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/types_udisk_price_data_set.go diff --git a/README.md b/README.md index da395c6192..5c40a826a8 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,8 @@ complete -F /usr/local/bin/ucloud ucloud ## Getting Started -Run the command to get started and configure ucloud-cli follow the steps. The public & private keys will be saved automatically and locally. +Run the command to get started and configure ucloud-cli follow the steps. The public & private keys will be saved automatically and locally to directory ~/.ucloud. +You can delete the directory whenever you want. ``` $ ucloud init diff --git a/base/client.go b/base/client.go index b61c2674b6..ecd923827f 100644 --- a/base/client.go +++ b/base/client.go @@ -3,6 +3,7 @@ package base import ( "github.com/ucloud/ucloud-sdk-go/services/pathx" "github.com/ucloud/ucloud-sdk-go/services/uaccount" + "github.com/ucloud/ucloud-sdk-go/services/udisk" "github.com/ucloud/ucloud-sdk-go/services/uhost" "github.com/ucloud/ucloud-sdk-go/services/ulb" "github.com/ucloud/ucloud-sdk-go/services/unet" @@ -19,6 +20,7 @@ type Client struct { ulb.ULBClient vpc.VPCClient pathx.PathXClient + udisk.UDiskClient } // NewClient will return a aggregate client @@ -30,5 +32,6 @@ func NewClient(config *ucloud.Config, credential *auth.Credential) *Client { *ulb.NewClient(config, credential), *vpc.NewClient(config, credential), *pathx.NewClient(config, credential), + *udisk.NewClient(config, credential), } } diff --git a/base/util.go b/base/util.go index 21b48b41d3..03078ead6e 100644 --- a/base/util.go +++ b/base/util.go @@ -15,6 +15,8 @@ import ( sdk "github.com/ucloud/ucloud-sdk-go/ucloud" uerr "github.com/ucloud/ucloud-sdk-go/ucloud/error" + "github.com/ucloud/ucloud-sdk-go/ucloud/helpers/waiter" + "github.com/ucloud/ucloud-sdk-go/ucloud/log" "github.com/ucloud/ucloud-sdk-go/ucloud/response" "github.com/ucloud/ucloud-cli/model" @@ -260,3 +262,59 @@ var RegionLabel = map[string]string{ "uk-london": "London", "afr-nigeria": "Lagos", } + +//Poll 轮询 +func Poll(describeFunc func(string, string, string, string) (interface{}, error)) func(string, string, string, string, []string) chan bool { + stateFields := []string{"State", "Status"} + return func(resourceID, projectID, region, zone string, targetState []string) chan bool { + w := waiter.StateWaiter{ + Pending: []string{"pending"}, + Target: []string{"avaliable"}, + Refresh: func() (interface{}, string, error) { + inst, err := describeFunc(resourceID, projectID, region, zone) + if err != nil { + return nil, "", err + } + + if inst == nil { + return nil, "pending", nil + } + instValue := reflect.ValueOf(inst) + instValue = reflect.Indirect(instValue) + instType := instValue.Type() + if instValue.Kind() != reflect.Struct { + return nil, "", fmt.Errorf("Instance is not struct") + } + state := "" + for i := 0; i < instValue.NumField(); i++ { + for _, sf := range stateFields { + if instType.Field(i).Name == sf { + state = instValue.Field(i).String() + } + } + } + if state != "" { + for _, t := range targetState { + if t == state { + return inst, "avaliable", nil + } + } + } + return nil, "pending", nil + + }, + Timeout: 5 * time.Minute, + } + + done := make(chan bool) + go func() { + if resp, err := w.Wait(); err != nil { + log.Error(err) + } else { + log.Infof("%#v", resp) + } + done <- true + }() + return done + } +} diff --git a/cmd/configure.go b/cmd/configure.go index 6a090286d6..8ca62f6386 100644 --- a/cmd/configure.go +++ b/cmd/configure.go @@ -51,7 +51,7 @@ func NewCmdInit() *cobra.Command { Cxt.Println(err) return } - if confirm { + if !confirm { printHello() return } diff --git a/cmd/disk.go b/cmd/disk.go new file mode 100644 index 0000000000..19ff0f5e5d --- /dev/null +++ b/cmd/disk.go @@ -0,0 +1,427 @@ +// Copyright © 2018 NAME HERE tony.li@ucloud.cn +// +// 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 cmd + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + sdk "github.com/ucloud/ucloud-sdk-go/ucloud" + + "github.com/ucloud/ucloud-cli/base" + "github.com/ucloud/ucloud-cli/model/status" + "github.com/ucloud/ucloud-cli/ux" +) + +//NewCmdDisk ucloud disk +func NewCmdDisk() *cobra.Command { + cmd := &cobra.Command{ + Use: "disk", + Short: "Read and manipulate ucloud disks", + Long: "Read and manipulate ucloud disks", + } + cmd.AddCommand(NewCmdDiskCreate()) + cmd.AddCommand(NewCmdDiskList()) + cmd.AddCommand(NewCmdDiskAttach()) + cmd.AddCommand(NewCmdDiskDetach()) + cmd.AddCommand(NewCmdDiskDelete()) + cmd.AddCommand(NewCmdDiskClone()) + cmd.AddCommand(NewCmdDiskExpand()) + return cmd +} + +//NewCmdDiskCreate ucloud disk create +func NewCmdDiskCreate() *cobra.Command { + req := base.BizClient.NewCreateUDiskRequest() + enableDataArk := sdk.String("false") + cmd := &cobra.Command{ + Use: "create", + Short: "Create ucloud disk", + Long: "Create ucloud disk", + Run: func(cmd *cobra.Command, args []string) { + if *enableDataArk == "true" { + req.UDataArkMode = sdk.String("Yes") + } else { + req.UDataArkMode = sdk.String("No") + } + + if *req.DiskType == "Oridinary" { + *req.DiskType = "DataDisk" + } else if *req.DiskType == "SSD" { + *req.DiskType = "SSDDataDisk" + } + + resp, err := base.BizClient.CreateUDisk(req) + if err != nil { + base.HandleError(err) + return + } + if count := len(resp.UDiskId); count == 1 { + text := fmt.Sprintf("udisk:%v is initializating", resp.UDiskId) + pollDisk(resp.UDiskId[0], *req.ProjectId, *req.Region, *req.Zone, text, []string{status.DISK_AVAILABLE, status.DISK_FAILED}) + } else if count > 1 { + base.Cxt.Printf("udisk:%v created\n", resp.UDiskId) + } else { + base.Cxt.PrintErr(fmt.Errorf("none disk created")) + } + }, + } + flags := cmd.Flags() + flags.SortFlags = false + req.ProjectId = flags.String("project-id", base.ConfigInstance.ProjectID, "Optional. Assign project-id") + req.Region = flags.String("region", base.ConfigInstance.Region, "Optional. Assign region") + req.Zone = flags.String("zone", base.ConfigInstance.Zone, "Optional. Assign availability zone") + req.Name = flags.String("name", "", "Required. Name of the disk to create") + req.Size = flags.Int("size-gb", 10, "Required. Size of the disk to create. Unit:GB. Normal disk [1,8000]; SSD disk [1,4000] ") + req.ChargeType = flags.String("charge-type", "Dynamic", "Optional.'Year',pay yearly;'Month',pay monthly;'Dynamic', pay hourly") + req.Quantity = flags.Int("quantity", 1, "Optional. The duration of the instance. N years/months.") + enableDataArk = flags.String("enable-data-ark", "false", "Optional. DataArk supports real-time backup, which can restore the disk back to any moment within the last 12 hours.") + req.Tag = flags.String("group", "Default", "Optional. Business group") + req.DiskType = flags.String("disk-type", "Oridinary", "Optional. 'Ordinary' or 'SSD'") + req.CouponId = flags.String("coupon-id", "", "Optional. Coupon ID, The Coupon can deduct part of the payment.See https://accountv2.ucloud.cn") + + flags.SetFlagValues("charge-type", "Month", "Year", "Dynamic", "Trial") + flags.SetFlagValues("enable-data-ark", "true", "false") + flags.SetFlagValues("disk-type", "Oridinary", "SSD") + + cmd.MarkFlagRequired("size-gb") + cmd.MarkFlagRequired("name") + + return cmd +} + +//DiskRow TableRow +type DiskRow struct { + ResourceID string + Name string + Group string + Size string + Type string + MountUHost string + MountPoint string + EnableDataArk string + State string + CreationTime string + ExpirationTime string +} + +//NewCmdDiskList ucloud disk list +func NewCmdDiskList() *cobra.Command { + req := base.BizClient.NewDescribeUDiskRequest() + typeMap := map[string]string{ + "DataDisk": "Oridinary-Data-Disk", + "SystemDisk": "Oridinary-System-Disk", + "SSDDataDisk": "SSD-Data-Disk", + } + arkModeMap := map[string]string{ + "Yes": "true", + "No": "false", + } + cmd := &cobra.Command{ + Use: "list", + Short: "List ucloud disk", + Long: "List ucloud disk", + Run: func(cmd *cobra.Command, args []string) { + for key, val := range typeMap { + if *req.DiskType == val { + *req.DiskType = key + } + } + resp, err := base.BizClient.DescribeUDisk(req) + if err != nil { + base.HandleError(err) + return + } + list := []DiskRow{} + for _, disk := range resp.DataSet { + row := DiskRow{ + ResourceID: disk.UDiskId, + Name: disk.Name, + Group: disk.Tag, + Size: fmt.Sprintf("%dGB", disk.Size), + Type: typeMap[disk.DiskType], + EnableDataArk: arkModeMap[disk.UDataArkMode], + MountUHost: disk.UHostIP, + MountPoint: fmt.Sprintf("%s", disk.DeviceName), + State: disk.Status, + CreationTime: base.FormatDate(disk.CreateTime), + ExpirationTime: base.FormatDate(disk.ExpiredTime), + } + list = append(list, row) + } + base.PrintTableS(list) + }, + } + flags := cmd.Flags() + flags.SortFlags = false + req.ProjectId = flags.String("project-id", base.ConfigInstance.ProjectID, "Optional. Assign project-id") + req.Region = flags.String("region", base.ConfigInstance.Region, "Optional. Assign region") + req.Zone = flags.String("zone", base.ConfigInstance.Zone, "Optional. Assign availability zone") + req.UDiskId = flags.String("resource-id", "", "Optional. Resource ID of the disk to search") + req.DiskType = flags.String("disk-type", "", "Optional. Optional. Type of the disk to search. 'Oridinary-Data-Disk','Oridinary-System-Disk' or 'SSD-Data-Disk'") + req.Offset = cmd.Flags().Int("offset", 0, "Optional. Offset") + req.Limit = cmd.Flags().Int("limit", 50, "Optional. Limit") + flags.SetFlagValues("disk-type", "Oridinary-Data-Disk", "Oridinary-System-Disk", "SSD-Data-Disk") + return cmd +} + +//NewCmdDiskAttach ucloud disk attach +func NewCmdDiskAttach() *cobra.Command { + req := base.BizClient.NewAttachUDiskRequest() + cmd := &cobra.Command{ + Use: "attach", + Short: "Attach a disk to an uhost instance", + Long: "Attach a disk to an uhost instance", + Run: func(cmd *cobra.Command, args []string) { + resp, err := base.BizClient.AttachUDisk(req) + if err != nil { + base.HandleError(err) + return + } + text := fmt.Sprintf("udisk[%s] is attaching to uhost uhost[%s]", *req.UDiskId, *req.UHostId) + pollDisk(resp.UDiskId, *req.ProjectId, *req.Region, *req.Zone, text, []string{status.DISK_INUSE, status.DISK_FAILED}) + }, + } + flags := cmd.Flags() + flags.SortFlags = false + req.ProjectId = flags.String("project-id", base.ConfigInstance.ProjectID, "Optional. Assign project-id") + req.Region = flags.String("region", base.ConfigInstance.Region, "Optional. Assign region") + req.Zone = flags.String("zone", base.ConfigInstance.Zone, "Optional. Assign availability zone") + req.UHostId = flags.String("uhost-id", "", "Resource ID of the uhost instance which you want to attach the disk") + req.UDiskId = flags.String("disk-id", "", "Resource ID of the udisk instance to attach") + + cmd.MarkFlagRequired("uhost-id") + cmd.MarkFlagRequired("disk-id") + + return cmd +} + +//NewCmdDiskDetach ucloud disk detach +func NewCmdDiskDetach() *cobra.Command { + req := base.BizClient.NewDetachUDiskRequest() + cmd := &cobra.Command{ + Use: "detach", + Short: "Detach an ucloud disk from uhost", + Long: "Detach an ucloud disk from uhost", + Run: func(cmd *cobra.Command, args []string) { + resp, err := base.BizClient.DetachUDisk(req) + if err != nil { + base.HandleError(err) + return + } + text := fmt.Sprintf("udisk[%s] is detaching from uhost[%s]", resp.UDiskId, resp.UHostId) + pollDisk(resp.UDiskId, *req.ProjectId, *req.Region, *req.Zone, text, []string{status.DISK_AVAILABLE, status.DISK_FAILED}) + }, + } + flags := cmd.Flags() + flags.SortFlags = false + req.ProjectId = flags.String("project-id", base.ConfigInstance.ProjectID, "Optional. Assign project-id") + req.Region = flags.String("region", base.ConfigInstance.Region, "Optional. Assign region") + req.Zone = flags.String("zone", base.ConfigInstance.Zone, "Optional. Assign availability zone") + req.UHostId = flags.String("uhost-id", "", "Resource ID of the uhost instance, from which you want to detach the disk") + req.UDiskId = flags.String("disk-id", "", "Resource ID of the udisk instance to detach") + + cmd.MarkFlagRequired("uhost-id") + cmd.MarkFlagRequired("disk-id") + return cmd +} + +//NewCmdDiskDelete ucloud disk delete +func NewCmdDiskDelete() *cobra.Command { + req := base.BizClient.NewDeleteUDiskRequest() + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete an ucloud disk", + Long: "Delete an ucloud disk", + Run: func(cmd *cobra.Command, args []string) { + if strings.Index(*req.UDiskId, "/") > -1 { + *req.UDiskId = strings.SplitN(*req.UDiskId, "/", 2)[0] + } + sure, err := ux.Prompt(fmt.Sprintf("Are you sure to delete disk[%s]?", *req.UDiskId)) + if err != nil { + base.Cxt.PrintErr(err) + return + } + if !sure { + return + } + _, err = base.BizClient.DeleteUDisk(req) + if err != nil { + base.HandleError(err) + return + } + base.Cxt.Printf("udisk[%s] deleted\n", *req.UDiskId) + }, + } + flags := cmd.Flags() + flags.SortFlags = false + req.ProjectId = flags.String("project-id", base.ConfigInstance.ProjectID, "Optional. Assign project-id") + req.Region = flags.String("region", base.ConfigInstance.Region, "Optional. Assign region") + req.Zone = flags.String("zone", base.ConfigInstance.Zone, "Optional. Assign availability zone") + req.UDiskId = flags.String("resource-id", "", "Required. The Resource ID of a disk to delete") + + flags.SetFlagValuesFunc("resource-id", func() []string { + return getDiskList([]string{status.DISK_AVAILABLE, status.DISK_FAILED}, *req.ProjectId, *req.Region, *req.Zone) + }) + + cmd.MarkFlagRequired("resource-id") + + return cmd +} + +//NewCmdDiskClone ucloud disk clone +func NewCmdDiskClone() *cobra.Command { + req := base.BizClient.NewCloneUDiskRequest() + enableDataArk := sdk.String("false") + cmd := &cobra.Command{ + Use: "clone", + Short: "Clone an ucloud disk", + Long: "Clone an ucloud disk", + Run: func(cmd *cobra.Command, args []string) { + if *enableDataArk == "true" { + req.UDataArkMode = sdk.String("Yes") + } else { + req.UDataArkMode = sdk.String("No") + } + if strings.Index(*req.SourceId, "/") > -1 { + *req.SourceId = strings.SplitN(*req.SourceId, "/", 2)[0] + } + resp, err := base.BizClient.CloneUDisk(req) + if err != nil { + base.HandleError(err) + return + } + if len(resp.UDiskId) == 1 { + pollText := fmt.Sprintf("cloned disk:[%s] is initializating", resp.UDiskId[0]) + pollDisk(resp.UDiskId[0], *req.ProjectId, *req.Region, *req.Zone, pollText, []string{status.DISK_AVAILABLE, status.DISK_FAILED}) + } else { + base.Cxt.Printf("disk[%v] cloned", resp.UDiskId) + } + }, + } + flags := cmd.Flags() + flags.SortFlags = false + req.SourceId = flags.String("source-id", "", "Required. Resource ID of parent disk") + req.Name = flags.String("name", "", "Required. Name of new disk") + req.ProjectId = flags.String("project-id", base.ConfigInstance.ProjectID, "Optional. Assign project-id") + req.Region = flags.String("region", base.ConfigInstance.Region, "Optional. Assign region") + req.Zone = flags.String("zone", base.ConfigInstance.Zone, "Optional. Assign availability zone") + req.ChargeType = flags.String("charge-type", "Month", "Optional.'Year',pay yearly;'Month',pay monthly;'Dynamic', pay hourly") + req.Quantity = flags.Int("quantity", 1, "Optional. The duration of the instance. N years/months.") + enableDataArk = flags.String("enable-data-ark", "false", "Optional. DataArk supports real-time backup, which can restore the disk back to any moment within the last 12 hours.") + req.CouponId = flags.String("coupon-id", "", "Optional. Coupon ID, The Coupon can deduct part of the payment,see https://accountv2.ucloud.cn") + + flags.SetFlagValues("charge-type", "Month", "Year", "Dynamic", "Trial") + flags.SetFlagValues("enable-data-ark", "true", "false") + + flags.SetFlagValuesFunc("source-id", func() []string { + return getDiskList([]string{status.DISK_AVAILABLE}, *req.ProjectId, *req.Region, *req.Zone) + }) + + cmd.MarkFlagRequired("source-id") + cmd.MarkFlagRequired("name") + + return cmd +} + +//NewCmdDiskExpand ucloud disk expand +func NewCmdDiskExpand() *cobra.Command { + req := base.BizClient.NewResizeUDiskRequest() + cmd := &cobra.Command{ + Use: "expand", + Short: "Expand disk size", + Long: "Expand disk size", + Run: func(cmd *cobra.Command, args []string) { + if strings.Index(*req.UDiskId, "/") > -1 { + *req.UDiskId = strings.SplitN(*req.UDiskId, "/", 2)[0] + } + if *req.Size > 8000 || *req.Size < 1 { + base.Cxt.Println("size-gb should be between 1 and 8000") + } + _, err := base.BizClient.ResizeUDisk(req) + if err != nil { + base.HandleError(err) + return + } + base.Cxt.Printf("disk:[%s] expanded to %d GB\n", *req.UDiskId, *req.Size) + }, + } + flags := cmd.Flags() + flags.SortFlags = false + req.UDiskId = flags.String("disk-id", "", "Required. Resource ID of the disk to expand") + req.Size = flags.Int("size-gb", 0, "Required. Size of the disk after expanded. Unit: GB. Range [1,8000]") + req.ProjectId = flags.String("project-id", base.ConfigInstance.ProjectID, "Optional. Assign project-id") + req.Region = flags.String("region", base.ConfigInstance.Region, "Optional. Assign region") + req.Zone = flags.String("zone", base.ConfigInstance.Zone, "Optional. Assign availability zone") + req.CouponId = flags.String("coupon-id", "", "Optional. Coupon ID, The Coupon can deduct part of the payment,see https://accountv2.ucloud.cn") + + flags.SetFlagValuesFunc("disk-id", func() []string { + return getDiskList([]string{status.DISK_AVAILABLE}, *req.ProjectId, *req.Region, *req.Zone) + }) + + cmd.MarkFlagRequired("disk-id") + cmd.MarkFlagRequired("size-gb") + + return cmd +} + +func getDiskList(states []string, project, region, zone string) []string { + req := base.BizClient.NewDescribeUDiskRequest() + req.ProjectId = sdk.String(project) + req.Region = sdk.String(region) + req.Zone = sdk.String(zone) + req.Limit = sdk.Int(50) + resp, err := base.BizClient.DescribeUDisk(req) + if err != nil { + //todo runtime log + return nil + } + list := []string{} + for _, disk := range resp.DataSet { + for _, s := range states { + if disk.Status == s { + list = append(list, disk.UDiskId+"/"+strings.Replace(disk.Name, " ", "-", -1)) + } + } + } + return list +} + +func pollDisk(resourceID, projectID, region, zone, pollText string, targetState []string) { + pollFunc := base.Poll(describeUdiskByID) + done := pollFunc(resourceID, projectID, region, zone, targetState) + ux.DotSpinner.Start(pollText) + <-done + ux.DotSpinner.Stop() +} + +func describeUdiskByID(udiskID, project, region, zone string) (interface{}, error) { + req := base.BizClient.NewDescribeUDiskRequest() + req.UDiskId = sdk.String(udiskID) + req.ProjectId = sdk.String(project) + req.Region = sdk.String(region) + req.Zone = sdk.String(zone) + req.Limit = sdk.Int(50) + resp, err := base.BizClient.DescribeUDisk(req) + if err != nil { + return nil, err + } + if len(resp.DataSet) < 1 { + return nil, nil + } + return &resp.DataSet[0], nil +} diff --git a/cmd/eip.go b/cmd/eip.go index df4deda857..4d5106d4e2 100644 --- a/cmd/eip.go +++ b/cmd/eip.go @@ -47,7 +47,7 @@ type EIPRow struct { Name string IP string ResourceID string - UGroup string + Group string Billing string Bandwidth string BindResource string @@ -79,15 +79,17 @@ func NewCmdEIPList() *cobra.Command { row.IP += ip.IP + " " + ip.OperatorName + " " } row.ResourceID = eip.EIPId - row.UGroup = eip.Tag + row.Group = eip.Tag row.Billing = eip.PayMode row.Bandwidth = strconv.Itoa(eip.Bandwidth) + "Mb" - row.BindResource = fmt.Sprintf("%s(%s)", eip.Resource.ResourceName, eip.Resource.ResourceType) + if eip.Resource.ResourceId != "" { + row.BindResource = fmt.Sprintf("%s|%s(%s)", eip.Resource.ResourceName, eip.Resource.ResourceId, eip.Resource.ResourceType) + } row.Status = eip.Status row.ExpirationTime = time.Unix(int64(eip.ExpireTime), 0).Format("2006-01-02") list = append(list, row) } - PrintTable(list, []string{"Name", "IP", "ResourceID", "UGroup", "Billing", "Bandwidth", "BindResource", "Status", "ExpirationTime"}) + PrintTable(list, []string{"Name", "IP", "ResourceID", "Group", "Billing", "Bandwidth", "BindResource", "Status", "ExpirationTime"}) } } }, @@ -114,9 +116,9 @@ func NewCmdEIPAllocate() *cobra.Command { HandleError(err) } else { for _, eip := range resp.EIPSet { - Cxt.Printf("EIPId:%s,", eip.EIPId) + Cxt.Printf("allocate EIP[%s] ", eip.EIPId) for _, ip := range eip.EIPAddr { - Cxt.Printf("IP:%s,Line:%s \n", ip.IP, ip.OperatorName) + Cxt.Printf("IP:%s Line:%s \n", ip.IP, ip.OperatorName) } } } @@ -128,9 +130,10 @@ func NewCmdEIPAllocate() *cobra.Command { req.OperatorName = cmd.Flags().String("line", "", "Required. 'BGP' or 'International'. 'BGP' could be set in China mainland regions, such as cn-bj2 etc. 'International' could be set in the regions beyond mainland, such as hk, tw-kh, us-ws etc.") req.Bandwidth = cmd.Flags().Int("bandwidth", 0, "Required. Bandwidth(Unit:Mbps).The range of value related to network charge mode. By traffic [1, 200]; by bandwidth [1,800] (Unit: Mbps); it could be 0 if the eip belong to the shared bandwidth") req.PayMode = cmd.Flags().String("charge-mode", "Bandwidth", "Optional. charge-mode is an enumeration value. 'Traffic','Bandwidth' or 'ShareBandwidth'") + req.ShareBandwidthId = cmd.Flags().String("share-bandwidth-id", "", "Optional. ShareBandwidthId, required only when charge-mode is 'ShareBandwidth'") req.Quantity = cmd.Flags().Int("quantity", 1, "Optional. The duration of the instance. N years/months.") req.ChargeType = cmd.Flags().String("charge-type", "Month", "Optional. Enumeration value.'Year',pay yearly;'Month',pay monthly;'Dynamic', pay hourly(requires permission),'Trial', free trial(need permission)") - req.Tag = cmd.Flags().String("ugroup", "Default", "UGroup of your EIP.") + req.Tag = cmd.Flags().String("group", "Default", "Group of your EIP.") req.Name = cmd.Flags().String("name", "EIP", "Name of your EIP.") req.Remark = cmd.Flags().String("remark", "", "Remark of your EIP.") req.CouponId = cmd.Flags().String("coupon-id", "", "Coupon ID, The Coupon can deducte part of the payment") @@ -144,32 +147,43 @@ func NewCmdEIPAllocate() *cobra.Command { //NewCmdEIPBind ucloud eip bind func NewCmdEIPBind() *cobra.Command { - var req = BizClient.NewBindEIPRequest() - var cmd = &cobra.Command{ + var projectID, region, eipID, resourceID, resourceType *string + cmd := &cobra.Command{ Use: "bind", Short: "Bind EIP with uhost", Long: "Bind EIP with uhost", Example: "ucloud eip bind --eip-id eip-xxx --resource-id uhost-xxx", Run: func(cmd *cobra.Command, args []string) { - req.ResourceType = sdk.String("uhost") - _, err := BizClient.BindEIP(req) - if err != nil { - HandleError(err) - } else { - Cxt.Printf("EIP: [%s] bind with %s:[%s] successfully \n", *req.EIPId, *req.ResourceType, *req.ResourceId) - } + bindEIP(resourceID, resourceType, eipID, projectID, region) }, } cmd.Flags().SortFlags = false - req.ProjectId = cmd.Flags().String("project-id", ConfigInstance.ProjectID, "Assign project-id") - req.Region = cmd.Flags().String("region", ConfigInstance.Region, "Assign region") - req.EIPId = cmd.Flags().String("eip-id", "", "EIPId to bind. Required") - req.ResourceId = cmd.Flags().String("resource-id", "", "ResourceID , which is the UHostId of uhost. Required") + projectID = cmd.Flags().String("project-id", ConfigInstance.ProjectID, "Assign project-id") + region = cmd.Flags().String("region", ConfigInstance.Region, "Assign region") + eipID = cmd.Flags().String("eip-id", "", "EIPId to bind. Required") + resourceID = cmd.Flags().String("resource-id", "", "ResourceID , which is the UHostId of uhost. Required") + resourceType = cmd.Flags().String("resource-type", "uhost", "ResourceType, type of resource to bind with eip. 'uhost','vrouter','ulb','upm','hadoophost'.eg..") cmd.MarkFlagRequired("eip-id") cmd.MarkFlagRequired("resource-id") + cmd.Flags().SetFlagValues("resource-type", "uhost", "vrouter", "ulb", "upm", "hadoophost", "fortresshost", "udockhost", "udhost", "natgw", "udb", "vpngw", "ucdr", "dbaudit") return cmd } +func bindEIP(resourceID, resourceType, eipID, projectID, region *string) { + req := BizClient.NewBindEIPRequest() + req.ResourceId = resourceID + req.ResourceType = resourceType + req.EIPId = eipID + req.ProjectId = projectID + req.Region = region + _, err := BizClient.BindEIP(req) + if err != nil { + HandleError(err) + } else { + Cxt.Printf("bind EIP[%s] with %s[%s]\n", *req.EIPId, *req.ResourceType, *req.ResourceId) + } +} + //NewCmdEIPUnbind ucloud eip unbind func NewCmdEIPUnbind() *cobra.Command { @@ -185,7 +199,7 @@ func NewCmdEIPUnbind() *cobra.Command { if err != nil { HandleError(err) } else { - Cxt.Printf("EIP: %s unbind with [%s]:[%s] successfully \n", *req.EIPId, *req.ResourceType, *req.ResourceId) + Cxt.Printf("unbind EIP[%s] with %s[%s]\n", *req.EIPId, *req.ResourceType, *req.ResourceId) } }, } @@ -216,7 +230,7 @@ func NewCmdEIPRelease() *cobra.Command { if err != nil { HandleError(err) } else { - Cxt.Printf("EIP: %v released \n", *req.EIPId) + Cxt.Printf("released EIP[%v]\n", *req.EIPId) } } }, diff --git a/cmd/globalssh.go b/cmd/globalssh.go index 4d95ae86a6..999e2d5725 100644 --- a/cmd/globalssh.go +++ b/cmd/globalssh.go @@ -15,6 +15,7 @@ package cmd import ( + "net" "strings" "github.com/spf13/cobra" @@ -150,6 +151,7 @@ var areaCodeMap = map[string]string{ //NewCmdGsshCreate ucloud gssh create func NewCmdGsshCreate() *cobra.Command { + var targetIP *net.IP req := BizClient.NewCreateGlobalSSHInstanceRequest() cmd := &cobra.Command{ Use: "create", @@ -167,17 +169,18 @@ func NewCmdGsshCreate() *cobra.Command { Cxt.Println("The port number should be between 1 and 65535, and cannot be 80 or 443") return } + req.TargetIP = sdk.String(targetIP.String()) resp, err := BizClient.CreateGlobalSSHInstance(req) if err != nil { HandleError(err) } else { - Cxt.Println("ResourceID:", resp.InstanceId) + Cxt.Printf("gssh[%s] created\n", resp.InstanceId) } }, } cmd.Flags().SortFlags = false req.AreaCode = cmd.Flags().String("location", "", "Required. Location of the source server. See 'ucloud gssh location'") - req.TargetIP = cmd.Flags().String("target-ip", "", "Required. IP of the source server. Required") + targetIP = cmd.Flags().IP("target-ip", nil, "Required. IP of the source server. Required") req.Region = cmd.Flags().String("region", "", "Optional. Assign region") req.ProjectId = cmd.Flags().String("project-id", ConfigInstance.ProjectID, "Optional. Assign project-id") req.Port = cmd.Flags().Int("port", 22, "Optional. Port of The SSH service between 1 and 65535. Do not use ports such as 80,443.") @@ -208,7 +211,7 @@ func NewCmdGsshDelete() *cobra.Command { if err != nil { HandleError(err) } else { - Cxt.Printf("GlobalSSH[%s] was successfully deleted\n", id) + Cxt.Printf("gssh[%s] deleted\n", id) } } }, @@ -251,7 +254,7 @@ func NewCmdGsshModify() *cobra.Command { if err != nil { HandleError(err) } else { - Cxt.Println("Successfully updated") + Cxt.Printf("gssh[%s] updated\n", *gsshModifyPortReq.InstanceId) } } if *gsshModifyRemarkReq.Remark != "" { @@ -259,7 +262,7 @@ func NewCmdGsshModify() *cobra.Command { if err != nil { HandleError(err) } else { - Cxt.Println("Successfully updated") + Cxt.Printf("gssh[%s] updated\n", *gsshModifyRemarkReq.InstanceId) } } }, diff --git a/cmd/root.go b/cmd/root.go index 26c9453238..5700b187a9 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -76,6 +76,7 @@ func NewCmdRoot() *cobra.Command { cmd.AddCommand(NewCmdSubnet()) cmd.AddCommand(NewCmdVPC()) cmd.AddCommand(NewCmdFirewall()) + cmd.AddCommand(NewCmdDisk()) return cmd } @@ -90,7 +91,8 @@ Examples: Commands:{{range .Commands}}{{if .IsAvailableCommand}} {{rpad .Name .NamePadding }} {{.Short}}{{end}} -{{end}}{{end}}{{if .HasAvailableLocalFlags}}Flags: + {{end}}{{end}}{{if .HasAvailableLocalFlags}} +Flags: {{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} Global Flags: diff --git a/cmd/uhost.go b/cmd/uhost.go index 0cb8a07880..1c65e29fbe 100644 --- a/cmd/uhost.go +++ b/cmd/uhost.go @@ -17,16 +17,14 @@ package cmd import ( "fmt" "strings" - "time" "github.com/spf13/cobra" "github.com/ucloud/ucloud-sdk-go/services/uhost" sdk "github.com/ucloud/ucloud-sdk-go/ucloud" - "github.com/ucloud/ucloud-sdk-go/ucloud/helpers/waiter" - "github.com/ucloud/ucloud-sdk-go/ucloud/log" - . "github.com/ucloud/ucloud-cli/base" + "github.com/ucloud/ucloud-cli/base" + "github.com/ucloud/ucloud-cli/model/status" "github.com/ucloud/ucloud-cli/ux" ) @@ -34,8 +32,8 @@ import ( func NewCmdUHost() *cobra.Command { cmd := &cobra.Command{ Use: "uhost", - Short: "List,create,delete,stop,restart,poweroff or scale UHost instance", - Long: `List,create,delete,stop,restart,poweroff or scale UHost instance`, + Short: "List,create,delete,stop,restart,poweroff or resize UHost instance", + Long: `List,create,delete,stop,restart,poweroff or resize UHost instance`, Args: cobra.NoArgs, } cmd.AddCommand(NewCmdUHostList()) @@ -45,7 +43,7 @@ func NewCmdUHost() *cobra.Command { cmd.AddCommand(NewCmdUHostStart()) cmd.AddCommand(NewCmdUHostReboot()) cmd.AddCommand(NewCmdUHostPoweroff()) - cmd.AddCommand(NewCmdUHostScale()) + cmd.AddCommand(NewCmdUHostResize()) return cmd } @@ -54,7 +52,7 @@ func NewCmdUHost() *cobra.Command { type UHostRow struct { UHostName string ResourceID string - UGroup string + Group string ClassicNetwork string Config string Type string @@ -64,26 +62,26 @@ type UHostRow struct { //NewCmdUHostList [ucloud uhost list] func NewCmdUHostList() *cobra.Command { - req := BizClient.NewDescribeUHostInstanceRequest() + req := base.BizClient.NewDescribeUHostInstanceRequest() cmd := &cobra.Command{ Use: "list", Short: "List all UHost Instances", Long: `List all UHost Instances`, Run: func(cmd *cobra.Command, args []string) { - resp, err := BizClient.DescribeUHostInstance(req) + resp, err := base.BizClient.DescribeUHostInstance(req) if err != nil { - HandleError(err) + base.HandleError(err) return } if global.json { - PrintJSON(resp.UHostSet) + base.PrintJSON(resp.UHostSet) } else { list := make([]UHostRow, 0) for _, host := range resp.UHostSet { row := UHostRow{} row.UHostName = host.Name row.ResourceID = host.UHostId - row.UGroup = host.Tag + row.Group = host.Tag for _, ip := range host.IPSet { if row.ClassicNetwork != "" { row.ClassicNetwork += " | " @@ -99,26 +97,26 @@ func NewCmdUHostList() *cobra.Command { memorySize := host.Memory / 1024 diskSize := 0 for _, disk := range host.DiskSet { - if disk.Type == "Data" { + if disk.Type == "Data" || disk.Type == "Udisk" { diskSize += disk.Size } } row.Config = fmt.Sprintf("%s cpu:%d memory:%dG disk:%dG", osName[0], cupCore, memorySize, diskSize) - row.CreationTime = FormatDate(host.CreateTime) + row.CreationTime = base.FormatDate(host.CreateTime) row.State = host.State row.Type = host.UHostType + "/" + host.HostType list = append(list, row) } - PrintTable(list, []string{"UHostName", "ResourceID", "UGroup", "ClassicNetwork", "Config", "Type", "CreationTime", "State"}) + base.PrintTable(list, []string{"UHostName", "ResourceID", "Group", "ClassicNetwork", "Config", "Type", "CreationTime", "State"}) } }, } cmd.Flags().SortFlags = false - req.ProjectId = cmd.Flags().String("project-id", ConfigInstance.ProjectID, "Optional. Assign project-id") - req.Region = cmd.Flags().String("region", ConfigInstance.Region, "Optional. Assign region") + req.ProjectId = cmd.Flags().String("project-id", base.ConfigInstance.ProjectID, "Optional. Assign project-id") + req.Region = cmd.Flags().String("region", base.ConfigInstance.Region, "Optional. Assign region") req.Zone = cmd.Flags().String("zone", "", "Optional. Assign availability zone") cmd.Flags().StringSliceVar(&req.UHostIds, "resource-id", make([]string, 0), "Optional. UHost Instance ID, multiple values separated by comma(without space)") - req.Tag = cmd.Flags().String("ugroup", "", "Optional. UGroup") + req.Tag = cmd.Flags().String("group", "", "Optional. Group") req.Offset = cmd.Flags().Int("offset", 0, "Optional. Offset default 0") req.Limit = cmd.Flags().Int("limit", 20, "Optional. Limit default 20, max value 100") @@ -127,7 +125,11 @@ func NewCmdUHostList() *cobra.Command { //NewCmdUHostCreate [ucloud uhost create] func NewCmdUHostCreate() *cobra.Command { - req := BizClient.NewCreateUHostInstanceRequest() + var bindEipID *string + req := base.BizClient.NewCreateUHostInstanceRequest() + eipReq := base.BizClient.NewAllocateEIPRequest() + + async := sdk.Bool(false) cmd := &cobra.Command{ Use: "create", Short: "Create UHost instance", @@ -139,12 +141,55 @@ func NewCmdUHostCreate() *cobra.Command { if len(images) >= 2 { *req.ImageId = images[0] } - resp, err := BizClient.CreateUHostInstance(req) + + resp, err := base.BizClient.CreateUHostInstance(req) if err != nil { - HandleError(err) + base.HandleError(err) return } - Cxt.Printf("UHost:%v created successfully!\n", resp.UHostIds) + + if !*async { + if len(resp.UHostIds) == 1 { + text := fmt.Sprintf("UHost:[%s] is initializing", resp.UHostIds[0]) + done := pollUhost(resp.UHostIds[0], *req.ProjectId, *req.Region, *req.Zone, []string{status.HOST_RUNNING, status.HOST_FAIL}) + ux.DotSpinner.Start(text) + <-done + ux.DotSpinner.Stop() + } + } else { + base.Cxt.Printf("UHost:%v created\n", resp.UHostIds) + } + + if *bindEipID != "" { + if len(resp.UHostIds) == 1 { + bindEIP(sdk.String(resp.UHostIds[0]), sdk.String("uhost"), bindEipID, req.ProjectId, req.Region) + } + } + + if *eipReq.OperatorName != "" && *eipReq.Bandwidth != 0 { + if *eipReq.OperatorName == "BGP" { + *eipReq.OperatorName = "Bgp" + } + eipReq.ChargeType = req.ChargeType + eipReq.Tag = req.Tag + eipReq.Quantity = req.Quantity + eipReq.Region = req.Region + eipReq.ProjectId = req.ProjectId + eipResp, err := base.BizClient.AllocateEIP(eipReq) + if err != nil { + base.HandleError(err) + } else { + for _, eip := range eipResp.EIPSet { + base.Cxt.Printf("allocate EIP[%s] ", eip.EIPId) + for _, ip := range eip.EIPAddr { + base.Cxt.Printf("IP:%s Line:%s \n", ip.IP, ip.OperatorName) + } + if len(resp.UHostIds) == 1 { + bindEIP(sdk.String(resp.UHostIds[0]), sdk.String("uhost"), sdk.String(eip.EIPId), req.ProjectId, req.Region) + } + } + } + } }, } @@ -155,73 +200,84 @@ func NewCmdUHostCreate() *cobra.Command { "hk-01": true, } defaultUhostType := "N2" - if _, ok := n1Zone[ConfigInstance.Zone]; ok { + if _, ok := n1Zone[base.ConfigInstance.Zone]; ok { defaultUhostType = "N1" } req.Disks = make([]uhost.UHostDisk, 2) - req.Disks[0].IsBoot = sdk.Bool(true) - req.Disks[1].IsBoot = sdk.Bool(false) + req.Disks[0].IsBoot = sdk.String("True") + req.Disks[1].IsBoot = sdk.String("False") flags := cmd.Flags() flags.SortFlags = false + async = flags.Bool("async", false, "Optional. Display information about the operation in progress, without waiting for the operation to complete.") req.CPU = flags.Int("cpu", 4, "Required. The count of CPU cores. Optional parameters: {1, 2, 4, 8, 12, 16, 24, 32}") - req.Memory = flags.Int("memory", 8, "Required. Memory size. Unit: GB. Range: [1, 128], multiple of 2") + req.Memory = flags.Int("memory-gb", 8, "Required. Memory size. Unit: GB. Range: [1, 128], multiple of 2") req.Password = flags.String("password", "", "Required. Password of the uhost user(root/ubuntu)") req.ImageId = flags.String("image-id", "", "Required. The ID of image. see 'ucloud image list'") req.VPCId = flags.String("vpc-id", "", "Optional. VPC ID. This field is required under VPC2.0. See 'ucloud vpc list'") req.SubnetId = flags.String("subnet-id", "", "Optional. Subnet ID. This field is required under VPC2.0. See 'ucloud subnet list'") req.Name = flags.String("name", "UHost", "Optional. UHost instance name") + bindEipID = flags.String("bind-eip-id", "", "Optional. Bind eip to uhost") + eipReq.OperatorName = flags.String("create-eip-line", "", "Optional. Required if you want to create new EIP. Line of created eip to bind with the uhost") + eipReq.Bandwidth = cmd.Flags().Int("create-eip-bandwidth-mb", 0, "Optional. Required if you want to create new EIP. Bandwidth(Unit:Mbps).The range of value related to network charge mode. By traffic [1, 200]; by bandwidth [1,800] (Unit: Mbps); it could be 0 if the eip belong to the shared bandwidth") + eipReq.PayMode = cmd.Flags().String("create-eip-charge-mode", "Bandwidth", "Optional. 'Traffic','Bandwidth' or 'ShareBandwidth'") + eipReq.Name = flags.String("create-eip-name", "", "Optional. Name of created eip to bind with the uhost") + eipReq.Remark = cmd.Flags().String("create-eip-remark", "", "Optional.Remark of your EIP.") + eipReq.CouponId = cmd.Flags().String("create-eip-coupon-id", "", "Optional.Coupon ID, The Coupon can deducte part of the payment,see https://accountv2.ucloud.cn") + req.ChargeType = flags.String("charge-type", "Month", "Optional.'Year',pay yearly;'Month',pay monthly;'Dynamic', pay hourly(requires access)") req.Quantity = flags.Int("quantity", 1, "Optional. The duration of the instance. N years/months.") - req.ProjectId = flags.String("project-id", ConfigInstance.ProjectID, "Optional. Assign project-id") - req.Region = flags.String("region", ConfigInstance.Region, "Optional. Assign region") - req.Zone = flags.String("zone", ConfigInstance.Zone, "Optional. Assign availability zone") + req.ProjectId = flags.String("project-id", base.ConfigInstance.ProjectID, "Optional. Assign project-id") + req.Region = flags.String("region", base.ConfigInstance.Region, "Optional. Assign region") + req.Zone = flags.String("zone", base.ConfigInstance.Zone, "Optional. Assign availability zone") req.UHostType = flags.String("type", defaultUhostType, "Optional. Default is 'N2' of which cpu is V4 and sata disk. also support 'N1' means V3 cpu and sata disk;'I2' means V4 cpu and ssd disk;'D1' means big data model;'G1' means GPU type, model for K80;'G2' model for P40; 'G3' model for V100") req.NetCapability = flags.String("net-capability", "Normal", "Optional. Default is 'Normal', also support 'Super' which will enhance multiple times network capability as before") - req.Disks[0].Type = flags.String("boot-disk-type", "LOCAL_NORMAL", "Optional. Enumeration value. 'LOCAL_NORMAL', Ordinary local disk; 'CLOUD_NORMAL', Ordinary cloud disk; 'LOCAL_SSD',local ssd disk; 'CLOUD_SSD',cloud ssd disk; 'EXCLUSIVE_LOCAL_DISK',big data. The disk only supports a limited combination.") - req.Disks[0].Size = flags.String("boot-disk-size", "20", "Optional. Default 20G. Windows should be bigger than 40G Unit GB") - req.Disks[0].BackupType = flags.String("boot-disk-backup-type", "NONE", "Optional. Enumeration value, 'NONE' or 'DATAARK'. DataArk supports real-time backup, which can restore the disk back to any moment within the last 12 hours. (Normal Local Disk and Normal Cloud Disk Only)") + req.Disks[0].Type = flags.String("os-disk-type", "LOCAL_NORMAL", "Optional. Enumeration value. 'LOCAL_NORMAL', Ordinary local disk; 'CLOUD_NORMAL', Ordinary cloud disk; 'LOCAL_SSD',local ssd disk; 'CLOUD_SSD',cloud ssd disk; 'EXCLUSIVE_LOCAL_DISK',big data. The disk only supports a limited combination.") + req.Disks[0].Size = flags.Int("os-disk-size-gb", 20, "Optional. Default 20G. Windows should be bigger than 40G Unit GB") + req.Disks[0].BackupType = flags.String("os-disk-backup-type", "NONE", "Optional. Enumeration value, 'NONE' or 'DATAARK'. DataArk supports real-time backup, which can restore the disk back to any moment within the last 12 hours. (Normal Local Disk and Normal Cloud Disk Only)") req.Disks[1].Type = flags.String("data-disk-type", "LOCAL_NORMAL", "Optional. Enumeration value. 'LOCAL_NORMAL', Ordinary local disk; 'CLOUD_NORMAL', Ordinary cloud disk; 'LOCAL_SSD',local ssd disk; 'CLOUD_SSD',cloud ssd disk; 'EXCLUSIVE_LOCAL_DISK',big data. The disk only supports a limited combination.") - req.Disks[1].Size = flags.String("data-disk-size", "20", "Optional. Disk size. Unit GB") + req.Disks[1].Size = flags.Int("data-disk-size-gb", 20, "Optional. Disk size. Unit GB") req.Disks[1].BackupType = flags.String("data-disk-backup-type", "NONE", "Optional. Enumeration value, 'NONE' or 'DATAARK'. DataArk supports real-time backup, which can restore the disk back to any moment within the last 12 hours. (Normal Local Disk and Normal Cloud Disk Only)") req.NetworkId = flags.String("network-id", "", "Optional. Network ID (no need to fill in the case of VPC2.0). In the case of VPC1.0, if not filled in, we will choose the basic network; if it is filled in, we will choose the subnet. See 'ucloud subnet list'.") req.SecurityGroupId = flags.String("firewall-id", "", "Optional. Firewall Id, default: Web recommended firewall. see 'ucloud firewall list'.") - req.Tag = flags.String("ugroup", "Default", "Optional. Business group") - req.CouponId = flags.String("coupon-id", "", "Optional. Coupon ID, The Coupon can deduct part of the payment,see DescribeCoupon or https://accountv2.ucloud.cn") + req.Tag = flags.String("group", "Default", "Optional. Business group") + req.CouponId = flags.String("coupon-id", "", "Optional. Coupon ID, The Coupon can deduct part of the payment,see https://accountv2.ucloud.cn") cmd.Flags().SetFlagValues("charge-type", "Month", "Year", "Dynamic", "Trial") cmd.Flags().SetFlagValues("cpu", "1", "2", "4", "8", "12", "16", "24", "32") cmd.Flags().SetFlagValues("type", "N2", "N1", "I2", "D1", "G1", "G2", "G3") cmd.Flags().SetFlagValues("net-capability", "Normal", "Super") - cmd.Flags().SetFlagValues("boot-disk-type", "LOCAL_NORMAL", "CLOUD_NORMAL", "LOCAL_SSD", "CLOUD_SSD", "EXCLUSIVE_LOCAL_DISK") - cmd.Flags().SetFlagValues("boot-disk-backup-type", "NONE", "DATAARK") + cmd.Flags().SetFlagValues("os-disk-type", "LOCAL_NORMAL", "CLOUD_NORMAL", "LOCAL_SSD", "CLOUD_SSD", "EXCLUSIVE_LOCAL_DISK") + cmd.Flags().SetFlagValues("os-disk-backup-type", "NONE", "DATAARK") cmd.Flags().SetFlagValues("data-disk-type", "LOCAL_NORMAL", "CLOUD_NORMAL", "LOCAL_SSD", "CLOUD_SSD", "EXCLUSIVE_LOCAL_DISK") cmd.Flags().SetFlagValues("data-disk-backup-type", "NONE", "DATAARK") + cmd.Flags().SetFlagValues("create-eip-line", "BGP", "International") + cmd.Flags().SetFlagValues("create-eip-charge-mode", "Bandwidth", "Traffic", "ShareBandwidth") cmd.Flags().SetFlagValuesFunc("image-id", func() []string { - req := BizClient.NewDescribeImageRequest() + req := base.BizClient.NewDescribeImageRequest() projectID, _ := flags.GetString("project-id") if projectID == "" { - projectID = ConfigInstance.ProjectID + projectID = base.ConfigInstance.ProjectID } req.ProjectId = sdk.String(projectID) region, _ := flags.GetString("region") if region == "" { - region = ConfigInstance.Region + region = base.ConfigInstance.Region } req.Region = sdk.String(region) zone, _ := flags.GetString("zone") if zone == "" { - zone = ConfigInstance.Zone + zone = base.ConfigInstance.Zone } req.Zone = sdk.String(zone) req.ImageType = sdk.String("Base") req.Limit = sdk.Int(1000) result := make([]string, 0) - resp, err := BizClient.DescribeImage(req) + resp, err := base.BizClient.DescribeImage(req) if err == nil { for _, image := range resp.ImageSet { if image.State == "Available" { @@ -244,17 +300,16 @@ func NewCmdUHostCreate() *cobra.Command { //NewCmdUHostDelete ucloud uhost delete func NewCmdUHostDelete() *cobra.Command { isDestory := sdk.Bool(false) - isEipReleased := sdk.Bool(false) - req := BizClient.NewTerminateUHostInstanceRequest() + req := base.BizClient.NewTerminateUHostInstanceRequest() cmd := &cobra.Command{ Use: "delete", Short: "Delete Uhost instance", Long: "Delete Uhost instance", Run: func(cmd *cobra.Command, args []string) { - sure, err := ux.Prompt("Are you sure you want to delete this host? (y/n):") + sure, err := ux.Prompt("Are you sure you want to delete this host?") if err != nil { - Cxt.Println(err) + base.Cxt.Println(err) return } if !sure { @@ -265,17 +320,13 @@ func NewCmdUHostDelete() *cobra.Command { } else { req.Destroy = sdk.Int(0) } - if *isEipReleased { - req.EIPReleased = sdk.String("yes") - } else { - req.EIPReleased = sdk.String("no") - } hostIns, err := describeUHostByID(*req.UHostId, *req.ProjectId, *req.Region, *req.Zone) if err != nil { - HandleError(err) + base.HandleError(err) } else if hostIns != nil { - if hostIns.State == "Running" { - _req := BizClient.NewStopUHostInstanceRequest() + ins := hostIns.(*uhost.UHostInstanceSet) + if ins.State == "Running" { + _req := base.BizClient.NewStopUHostInstanceRequest() _req.ProjectId = req.ProjectId _req.Region = req.Region _req.Zone = req.Zone @@ -283,21 +334,21 @@ func NewCmdUHostDelete() *cobra.Command { stopUhostIns(_req) } } - resp, err := BizClient.TerminateUHostInstance(req) + resp, err := base.BizClient.TerminateUHostInstance(req) if err != nil { - HandleError(err) + base.HandleError(err) } else { - Cxt.Printf("UHost:[%v] deleted successfully!\n", resp.UHostId) + base.Cxt.Printf("UHost:[%v] deleted\n", resp.UHostId) } }, } - req.ProjectId = cmd.Flags().String("project-id", ConfigInstance.ProjectID, "Assign project-id") - req.Region = cmd.Flags().String("region", ConfigInstance.Region, "Assign region") + req.ProjectId = cmd.Flags().String("project-id", base.ConfigInstance.ProjectID, "Assign project-id") + req.Region = cmd.Flags().String("region", base.ConfigInstance.Region, "Assign region") req.Zone = cmd.Flags().String("zone", "", "availability zone") req.UHostId = cmd.Flags().String("resource-id", "", "ResourceID of the uhost instance( or UHostId)") isDestory = cmd.Flags().Bool("destory", false, "false,the uhost instance will be thrown to UHost recycle If you have permission; true,the uhost instance will be deleted directly") - isEipReleased = cmd.Flags().Bool("eip-released", false, "false,Unbind EIP only; true, Unbind EIP and release it") + req.EIPReleased = cmd.Flags().String("eip-released", "false", "false,Unbind EIP only; true, Unbind EIP and release it") cmd.Flags().SetFlagValues("destory", "true", "false") cmd.Flags().SetFlagValues("eip-released", "true", "false") cmd.MarkFlagRequired("resource-id") @@ -307,7 +358,7 @@ func NewCmdUHostDelete() *cobra.Command { //NewCmdUHostStop ucloud uhost stop func NewCmdUHostStop() *cobra.Command { - req := BizClient.NewStopUHostInstanceRequest() + req := base.BizClient.NewStopUHostInstanceRequest() cmd := &cobra.Command{ Use: "stop", Short: "Shut down uhost instance", @@ -316,8 +367,8 @@ func NewCmdUHostStop() *cobra.Command { stopUhostIns(req) }, } - req.ProjectId = cmd.Flags().String("project-id", ConfigInstance.ProjectID, "Assign project-id") - req.Region = cmd.Flags().String("region", ConfigInstance.Region, "Assign region") + req.ProjectId = cmd.Flags().String("project-id", base.ConfigInstance.ProjectID, "Assign project-id") + req.Region = cmd.Flags().String("region", base.ConfigInstance.Region, "Assign region") req.Zone = cmd.Flags().String("zone", "", "Assign availability zone") req.UHostId = cmd.Flags().String("resource-id", "", "ResourceID of the uhost instance( or UHostId)") cmd.MarkFlagRequired("resource-id") @@ -326,12 +377,12 @@ func NewCmdUHostStop() *cobra.Command { } func stopUhostIns(req *uhost.StopUHostInstanceRequest) { - resp, err := BizClient.StopUHostInstance(req) + resp, err := base.BizClient.StopUHostInstance(req) if err != nil { - HandleError(err) + base.HandleError(err) } else { text := fmt.Sprintf("UHost:[%v] is shuting down", resp.UhostId) - done := poll(resp.UhostId, *req.ProjectId, *req.Region, *req.Zone, "Stopped") + done := pollUhost(resp.UhostId, *req.ProjectId, *req.Region, *req.Zone, []string{status.HOST_STOPPED, status.HOST_FAIL}) ux.DotSpinner.Start(text) <-done ux.DotSpinner.Stop() @@ -340,27 +391,27 @@ func stopUhostIns(req *uhost.StopUHostInstanceRequest) { //NewCmdUHostStart ucloud uhost start func NewCmdUHostStart() *cobra.Command { - req := BizClient.NewStartUHostInstanceRequest() + req := base.BizClient.NewStartUHostInstanceRequest() cmd := &cobra.Command{ Use: "start", Short: "Start Uhost instance", Long: "Start Uhost instance", Example: "ucloud uhost start --resource-id uhost-xxx", Run: func(cmd *cobra.Command, args []string) { - resp, err := BizClient.StartUHostInstance(req) + resp, err := base.BizClient.StartUHostInstance(req) if err != nil { - HandleError(err) + base.HandleError(err) } else { text := fmt.Sprintf("UHost:[%v] is starting", resp.UhostId) - done := poll(resp.UhostId, *req.ProjectId, *req.Region, *req.Zone, "Running") + done := pollUhost(resp.UhostId, *req.ProjectId, *req.Region, *req.Zone, []string{status.HOST_RUNNING, status.HOST_FAIL}) ux.DotSpinner.Start(text) <-done ux.DotSpinner.Stop() } }, } - req.ProjectId = cmd.Flags().String("project-id", ConfigInstance.ProjectID, "Assign project-id") - req.Region = cmd.Flags().String("region", ConfigInstance.Region, "Assign region") + req.ProjectId = cmd.Flags().String("project-id", base.ConfigInstance.ProjectID, "Assign project-id") + req.Region = cmd.Flags().String("region", base.ConfigInstance.Region, "Assign region") req.Zone = cmd.Flags().String("zone", "", "Assign availability zone") req.UHostId = cmd.Flags().String("resource-id", "", "ResourceID of the uhost instance( or UHostId)") req.DiskPassword = cmd.Flags().String("disk-password", "", "Encrypted disk password") @@ -370,27 +421,27 @@ func NewCmdUHostStart() *cobra.Command { //NewCmdUHostReboot ucloud uhost restart func NewCmdUHostReboot() *cobra.Command { - req := BizClient.NewRebootUHostInstanceRequest() + req := base.BizClient.NewRebootUHostInstanceRequest() cmd := &cobra.Command{ Use: "restart", Short: "Restart/reboot Uhost instance", Long: "Restart/reboot Uhost instance", Example: "ucloud uhost restart --resource-id uhost-xxx", Run: func(cmd *cobra.Command, args []string) { - resp, err := BizClient.RebootUHostInstance(req) + resp, err := base.BizClient.RebootUHostInstance(req) if err != nil { - HandleError(err) + base.HandleError(err) } else { text := fmt.Sprintf("UHost:[%v] is restarting", resp.UhostId) - done := poll(resp.UhostId, *req.ProjectId, *req.Region, *req.Zone, "Running") + done := pollUhost(resp.UhostId, *req.ProjectId, *req.Region, *req.Zone, []string{status.HOST_RUNNING, status.HOST_FAIL}) ux.DotSpinner.Start(text) <-done ux.DotSpinner.Stop() } }, } - req.ProjectId = cmd.Flags().String("project-id", ConfigInstance.ProjectID, "Assign project-id") - req.Region = cmd.Flags().String("region", ConfigInstance.Region, "Assign region") + req.ProjectId = cmd.Flags().String("project-id", base.ConfigInstance.ProjectID, "Assign project-id") + req.Region = cmd.Flags().String("region", base.ConfigInstance.Region, "Assign region") req.Zone = cmd.Flags().String("zone", "", "Assign availability zone") req.UHostId = cmd.Flags().String("resource-id", "", "ResourceID of the uhost instance( or UHostId)") req.DiskPassword = cmd.Flags().String("disk-password", "", "Encrypted disk password") @@ -400,7 +451,7 @@ func NewCmdUHostReboot() *cobra.Command { //NewCmdUHostPoweroff ucloud uhost poweroff func NewCmdUHostPoweroff() *cobra.Command { - req := BizClient.NewPoweroffUHostInstanceRequest() + req := base.BizClient.NewPoweroffUHostInstanceRequest() cmd := &cobra.Command{ Use: "poweroff", Short: "Analog power off Uhost instnace", @@ -408,34 +459,34 @@ func NewCmdUHostPoweroff() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { sure, err := ux.Prompt("Danger, it may affect data integrity. Are you sure you want to poweroff this host? (y/n):") if err != nil { - Cxt.Println(err) + base.Cxt.Println(err) return } if sure { - resp, err := BizClient.PoweroffUHostInstance(req) + resp, err := base.BizClient.PoweroffUHostInstance(req) if err != nil { - HandleError(err) + base.HandleError(err) } else { - Cxt.Printf("UHost:[%v] is power off\n", resp.UhostId) + base.Cxt.Printf("UHost:[%v] is power off\n", resp.UhostId) } } }, } - req.ProjectId = cmd.Flags().String("project-id", ConfigInstance.ProjectID, "Assign project-id") - req.Region = cmd.Flags().String("region", ConfigInstance.Region, "Assign region") + req.ProjectId = cmd.Flags().String("project-id", base.ConfigInstance.ProjectID, "Assign project-id") + req.Region = cmd.Flags().String("region", base.ConfigInstance.Region, "Assign region") req.Zone = cmd.Flags().String("zone", "", "Assign availability zone") req.UHostId = cmd.Flags().String("resource-id", "", "ResourceID of the uhost instance( or UHostId)") cmd.MarkFlagRequired("resource-id") return cmd } -//NewCmdUHostScale ucloud uhost scale -func NewCmdUHostScale() *cobra.Command { - req := BizClient.NewResizeUHostInstanceRequest() +//NewCmdUHostResize ucloud uhost resize +func NewCmdUHostResize() *cobra.Command { + req := base.BizClient.NewResizeUHostInstanceRequest() cmd := &cobra.Command{ - Use: "scale", - Short: "Scale uhost instance,such as cpu core count, memory size and disk size", - Long: "Scale uhost instance,such as cpu core count, memory size and disk size", + Use: "resize", + Short: "Resize uhost instance,such as cpu core count, memory size and disk size", + Long: "Resize uhost instance,such as cpu core count, memory size and disk size", Run: func(cmd *cobra.Command, args []string) { if *req.CPU == 0 { req.CPU = nil @@ -453,17 +504,18 @@ func NewCmdUHostScale() *cobra.Command { } host, err := describeUHostByID(*req.UHostId, *req.ProjectId, *req.Region, *req.Zone) if err != nil { - Cxt.Println(err) + base.Cxt.Println(err) return } - if host.State == "Running" { - agreeClose, err := ux.Prompt("Scale uhost must be after stop it. Do you want to stop this host? (y/n):") + inst := host.(*uhost.UHostInstanceSet) + if inst.State == "Running" { + agreeClose, err := ux.Prompt("Resize uhost must be after stop it. Do you want to stop this host? (y/n):") if err != nil { - Cxt.Println(err) + base.Cxt.Println(err) return } if agreeClose { - _req := BizClient.NewStopUHostInstanceRequest() + _req := base.BizClient.NewStopUHostInstanceRequest() _req.ProjectId = req.ProjectId _req.Region = req.Region _req.Zone = req.Zone @@ -472,66 +524,37 @@ func NewCmdUHostScale() *cobra.Command { } } - resp, err := BizClient.ResizeUHostInstance(req) + resp, err := base.BizClient.ResizeUHostInstance(req) if err != nil { - HandleError(err) + base.HandleError(err) } else { - Cxt.Printf("UHost:[%v] scaled\n", resp.UhostId) + base.Cxt.Printf("UHost:[%v] resized\n", resp.UhostId) } }, } - req.ProjectId = cmd.Flags().String("project-id", ConfigInstance.ProjectID, "Assign project-id") - req.Region = cmd.Flags().String("region", ConfigInstance.Region, "Assign region") + req.ProjectId = cmd.Flags().String("project-id", base.ConfigInstance.ProjectID, "Assign project-id") + req.Region = cmd.Flags().String("region", base.ConfigInstance.Region, "Assign region") req.Zone = cmd.Flags().String("zone", "", "Assign availability zone") req.UHostId = cmd.Flags().String("resource-id", "", "ResourceID of the uhost instance( or UHostId)") req.CPU = cmd.Flags().Int("cpu", 0, "The number of virtual CPU cores. Series1 {1, 2, 4, 8, 12, 16, 24, 32}. Series2 {1,2,4,8,16}") req.Memory = cmd.Flags().Int("memory", 0, "memory size. Unit: GB. Range: [1, 128], multiple of 2") - req.DiskSpace = cmd.Flags().Int("data-disk-size", 0, "Data disk size,unit GB. Range[10,1000], SSD disk range[100,500]. Step 10") - req.BootDiskSpace = cmd.Flags().Int("system-disk-size", 0, "System disk size, unit GB. Range[20,100]. Step 10. System disk does not support shrinkage") + req.DiskSpace = cmd.Flags().Int("data-disk-size-gb", 0, "Data disk size,unit GB. Range[10,1000], SSD disk range[100,500]. Step 10") + req.BootDiskSpace = cmd.Flags().Int("system-disk-size-gb", 0, "System disk size, unit GB. Range[20,100]. Step 10. System disk does not support shrinkage") req.NetCapValue = cmd.Flags().Int("net-cap", 0, "NIC scale. 1,upgrade; 2,downgrade; 0,unchanged") cmd.MarkFlagRequired("resource-id") return cmd } -func poll(uhostID, projectID, region, zone string, targetState string) chan bool { - w := waiter.StateWaiter{ - Pending: []string{"pending"}, - Target: []string{"avaliable"}, - Refresh: func() (interface{}, string, error) { - inst, err := describeUHostByID(uhostID, projectID, region, zone) - if err != nil { - return nil, "", err - } - - if inst == nil || inst.State != targetState { - return nil, "pending", nil - } - - return inst, "avaliable", nil - }, - Timeout: 5 * time.Minute, - } - - done := make(chan bool) - go func() { - if resp, err := w.Wait(); err != nil { - log.Error(err) - } else { - log.Infof("%#v", resp) - } - done <- true - }() - return done -} +var pollUhost = base.Poll(describeUHostByID) -func describeUHostByID(uhostID, projectID, region, zone string) (*uhost.UHostInstanceSet, error) { - req := BizClient.NewDescribeUHostInstanceRequest() +func describeUHostByID(uhostID, projectID, region, zone string) (interface{}, error) { + req := base.BizClient.NewDescribeUHostInstanceRequest() req.UHostIds = []string{uhostID} req.ProjectId = &projectID req.Region = ®ion req.Zone = &zone - resp, err := BizClient.DescribeUHostInstance(req) + resp, err := base.BizClient.DescribeUHostInstance(req) if err != nil { return nil, err } diff --git a/cmd/unet.go b/cmd/unet.go index c7160b47ea..444b507418 100644 --- a/cmd/unet.go +++ b/cmd/unet.go @@ -39,7 +39,7 @@ func NewCmdSubnet() *cobra.Command { type SubnetRow struct { SubnetName string ResourceID string - UGroup string + Group string AffiliatedVPC string NetworkSegment string CreationTime string @@ -67,13 +67,13 @@ func NewCmdSubnetList() *cobra.Command { row := SubnetRow{} row.SubnetName = sn.SubnetName row.ResourceID = sn.SubnetId - row.UGroup = sn.Tag + row.Group = sn.Tag row.AffiliatedVPC = fmt.Sprintf("%s/%s", sn.VPCName, sn.VPCId) row.NetworkSegment = fmt.Sprintf("%s/%s", sn.Subnet, sn.Netmask) row.CreationTime = FormatDate(sn.CreateTime) list = append(list, row) } - PrintTable(list, []string{"SubnetName", "ResourceID", "UGroup", "AffiliatedVPC", "NetworkSegment", "CreationTime"}) + PrintTable(list, []string{"SubnetName", "ResourceID", "Group", "AffiliatedVPC", "NetworkSegment", "CreationTime"}) } }, } @@ -84,7 +84,7 @@ func NewCmdSubnetList() *cobra.Command { req.ProjectId = flags.String("project-id", ConfigInstance.ProjectID, "Optional. Project-id, see 'ucloud project list'") flags.StringSliceVar(&req.SubnetIds, "subnet-id", []string{}, "Optional. Multiple values separated by commas") req.VPCId = flags.String("vpc-id", "", "Optional. ResourceID of VPC") - req.Tag = flags.String("ugroup", "", "Optional. UGroup") + req.Tag = flags.String("group", "", "Optional. Group") req.Offset = flags.Int("offset", 0, "Optional. offset default 0") req.Limit = flags.Int("limit", 50, "Optional. max count") @@ -108,7 +108,7 @@ func NewCmdVPC() *cobra.Command { type VPCRow struct { VPCName string ResourceID string - UGroup string + Group string NetworkSegment string SubnetCount int CreationTime string @@ -136,13 +136,13 @@ func NewCmdVPCList() *cobra.Command { row := VPCRow{} row.VPCName = vpc.Name row.ResourceID = vpc.VPCId - row.UGroup = vpc.Tag + row.Group = vpc.Tag row.NetworkSegment = strings.Join(vpc.Network, ",") row.SubnetCount = vpc.SubnetCount row.CreationTime = FormatDate(vpc.CreateTime) list = append(list, row) } - PrintTable(list, []string{"VPCName", "ResourceID", "UGroup", "NetworkSegment", "SubnetCount", "CreationTime"}) + PrintTable(list, []string{"VPCName", "ResourceID", "Group", "NetworkSegment", "SubnetCount", "CreationTime"}) } }, @@ -151,7 +151,7 @@ func NewCmdVPCList() *cobra.Command { flags.SortFlags = false req.Region = flags.String("region", ConfigInstance.Region, "Optional. Region, see 'ucloud region'") req.ProjectId = flags.String("project-id", ConfigInstance.ProjectID, "Optional. Project-id, see 'ucloud project list'") - req.Tag = flags.String("ugroup", "", "Optional. UGroup") + req.Tag = flags.String("group", "", "Optional. Group") flags.StringSliceVar(&req.VPCIds, "vpc-id", []string{}, "Optional. Multiple values separated by commas") return cmd @@ -175,7 +175,7 @@ type FirewallRow struct { ResourceID string FirewallName string Remark string - UGroup string + Group string RuleAmount int BoundResourceAmount int CreationTime string @@ -203,13 +203,13 @@ func NewCmdFirewallList() *cobra.Command { row.ResourceID = fw.FWId row.FirewallName = fw.Name row.Remark = fw.Remark - row.UGroup = fw.Tag + row.Group = fw.Tag row.RuleAmount = len(fw.Rule) row.BoundResourceAmount = fw.ResourceCount row.CreationTime = FormatDate(fw.CreateTime) list = append(list, row) } - PrintTable(list, []string{"ResourceID", "FirewallName", "Remark", "UGroup", "RuleAmount", "BoundResourceAmount", "CreationTime"}) + PrintTable(list, []string{"ResourceID", "FirewallName", "Remark", "Group", "RuleAmount", "BoundResourceAmount", "CreationTime"}) } }, } diff --git a/model/status/status.go b/model/status/status.go new file mode 100644 index 0000000000..34431a2fb2 --- /dev/null +++ b/model/status/status.go @@ -0,0 +1,9 @@ +package status + +const HOST_RUNNING = "Running" +const HOST_STOPPED = "Stopped" +const HOST_FAIL = "Install Fail" + +const DISK_INUSE = "InUse" +const DISK_AVAILABLE = "Available" +const DISK_FAILED = "Failed" diff --git a/ux/prompt.go b/ux/prompt.go index b49fe1bcdd..ba2a93cae2 100644 --- a/ux/prompt.go +++ b/ux/prompt.go @@ -9,6 +9,9 @@ import ( // Prompt confirm func Prompt(text string) (bool, error) { + if !strings.HasSuffix(text, "(y/n):") { + text += " (y/n):" + } base.Cxt.Printf(text) var agreeClose string _, err := fmt.Scanf("%s\n", &agreeClose) diff --git a/vendor/github.com/spf13/cobra/command.go b/vendor/github.com/spf13/cobra/command.go index 2c6cbe48ab..c7b309715c 100644 --- a/vendor/github.com/spf13/cobra/command.go +++ b/vendor/github.com/spf13/cobra/command.go @@ -991,6 +991,7 @@ func (c *Command) complete() error { p = length } compLine := string(chars[0:p]) + chars = []rune(compLine) lastSpaceIndex := 0 for i, r := range chars { if r == ' ' { diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/attach_udisk.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/attach_udisk.go new file mode 100644 index 0000000000..65b6460ae1 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/attach_udisk.go @@ -0,0 +1,59 @@ +//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors. +//go:generate ucloud-gen-go-api UDisk AttachUDisk + +package udisk + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// AttachUDiskRequest is request schema for AttachUDisk action +type AttachUDiskRequest struct { + request.CommonBase + + // 可用区。参见 [可用区列表](../summary/regionlist.html) + Zone *string `required:"true"` + + // UHost实例ID + UHostId *string `required:"true"` + + // 需要挂载的UDisk实例ID + UDiskId *string `required:"true"` +} + +// AttachUDiskResponse is response schema for AttachUDisk action +type AttachUDiskResponse struct { + response.CommonBase + + // 挂载的UHost实例ID + UHostId string + + // 挂载的UDisk实例ID + UDiskId string +} + +// NewAttachUDiskRequest will create request of AttachUDisk action. +func (c *UDiskClient) NewAttachUDiskRequest() *AttachUDiskRequest { + req := &AttachUDiskRequest{} + + // setup request with client config + c.client.SetupRequest(req) + + // setup retryable with default retry policy (retry for non-create action and common error) + req.SetRetryable(true) + return req +} + +// AttachUDisk - 将一个可用的UDisk挂载到某台主机上,当UDisk挂载成功后,还需要在主机内部进行文件系统操作 +func (c *UDiskClient) AttachUDisk(req *AttachUDiskRequest) (*AttachUDiskResponse, error) { + var err error + var res AttachUDiskResponse + + err = c.client.InvokeAction("AttachUDisk", req, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/client.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/client.go new file mode 100644 index 0000000000..a58904bf91 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/client.go @@ -0,0 +1,19 @@ +package udisk + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud" + "github.com/ucloud/ucloud-sdk-go/ucloud/auth" +) + +// UDiskClient is the client of ucloud disk +type UDiskClient struct { + client *ucloud.Client +} + +// NewClient will create an instance of UDiskClient +func NewClient(config *ucloud.Config, credential *auth.Credential) *UDiskClient { + client := ucloud.NewClient(config, credential) + return &UDiskClient{ + client: client, + } +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/clone_udisk.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/clone_udisk.go new file mode 100644 index 0000000000..b2a0ca80f9 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/clone_udisk.go @@ -0,0 +1,71 @@ +//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors. +//go:generate ucloud-gen-go-api UDisk CloneUDisk + +package udisk + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// CloneUDiskRequest is request schema for CloneUDisk action +type CloneUDiskRequest struct { + request.CommonBase + + // 可用区。参见 [可用区列表](../summary/regionlist.html) + Zone *string `required:"true"` + + // 实例名称 + Name *string `required:"true"` + + // 克隆父Disk的Id + SourceId *string `required:"true"` + + // 方舟是否开启,"Yes":开启,"No":关闭;默认为"No" + UDataArkMode *string `required:"false"` + + // 购买时长 默认: 1 + Quantity *int `required:"false"` + + // Disk注释 + Comment *string `required:"false"` + + // Year , Month, Dynamic 默认: Dynamic + ChargeType *string `required:"false"` + + // 使用的代金券id + CouponId *string `required:"false"` +} + +// CloneUDiskResponse is response schema for CloneUDisk action +type CloneUDiskResponse struct { + response.CommonBase + + // 创建UDisk Id + UDiskId []string +} + +// NewCloneUDiskRequest will create request of CloneUDisk action. +func (c *UDiskClient) NewCloneUDiskRequest() *CloneUDiskRequest { + req := &CloneUDiskRequest{} + + // setup request with client config + c.client.SetupRequest(req) + + // setup retryable with default retry policy (retry for non-create action and common error) + req.SetRetryable(false) + return req +} + +// CloneUDisk - 从UDisk创建UDisk克隆 +func (c *UDiskClient) CloneUDisk(req *CloneUDiskRequest) (*CloneUDiskResponse, error) { + var err error + var res CloneUDiskResponse + + err = c.client.InvokeAction("CloneUDisk", req, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/clone_udisk_snapshot.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/clone_udisk_snapshot.go new file mode 100644 index 0000000000..013df95b6b --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/clone_udisk_snapshot.go @@ -0,0 +1,69 @@ +//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors. +//go:generate ucloud-gen-go-api UDisk CloneUDiskSnapshot + +package udisk + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// CloneUDiskSnapshotRequest is request schema for CloneUDiskSnapshot action +type CloneUDiskSnapshotRequest struct { + request.CommonBase + + // 可用区。参见 [可用区列表](../summary/regionlist.html) + Zone *string `required:"true"` + + // 实例名称 + Name *string `required:"true"` + + // 克隆父Snapshot的Id + SourceId *string `required:"true"` + + // 购买UDisk大小,单位:GB,范围[1~2000], 权限位控制可达8T,若需要请申请开通相关权限。 + Size *int `required:"true"` + + // Disk注释 + Comment *string `required:"false"` + + // Year , Month, Dynamic 默认: Dynamic + ChargeType *string `required:"false"` + + // 购买时长 默认: 1 + Quantity *int `required:"false"` + + // 是否开启数据方舟 默认:No + UDataArkMode *string `required:"false"` + + // 使用的代金券id + CouponId *string `required:"false"` +} + +// CloneUDiskSnapshotResponse is response schema for CloneUDiskSnapshot action +type CloneUDiskSnapshotResponse struct { + response.CommonBase + + // 创建UDisk Id + UDiskId []string +} + +// NewCloneUDiskSnapshotRequest will create request of CloneUDiskSnapshot action. +func (c *UDiskClient) NewCloneUDiskSnapshotRequest() *CloneUDiskSnapshotRequest { + req := &CloneUDiskSnapshotRequest{} + c.client.SetupRequest(req) + return req +} + +// CloneUDiskSnapshot - 从快照创建UDisk克隆 +func (c *UDiskClient) CloneUDiskSnapshot(req *CloneUDiskSnapshotRequest) (*CloneUDiskSnapshotResponse, error) { + var err error + var res CloneUDiskSnapshotResponse + + err = c.client.InvokeAction("CloneUDiskSnapshot", req, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/create_udisk.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/create_udisk.go new file mode 100644 index 0000000000..87c7b1a9cd --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/create_udisk.go @@ -0,0 +1,74 @@ +//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors. +//go:generate ucloud-gen-go-api UDisk CreateUDisk + +package udisk + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// CreateUDiskRequest is request schema for CreateUDisk action +type CreateUDiskRequest struct { + request.CommonBase + + // 可用区。参见 [可用区列表](../summary/regionlist.html) + Zone *string `required:"true"` + + // 购买UDisk大小,单位:GB,普通盘: 范围[1~2000], 权限位控制可达8T,若需要请申请开通相关权限;SSD盘: 范围[1~4000]。 + Size *int `required:"true"` + + // 实例名称 + Name *string `required:"true"` + + // Year , Month, Dynamic, Trial 默认: Dynamic + ChargeType *string `required:"false"` + + // 购买时长 默认: 1 + Quantity *int `required:"false"` + + // 是否开启数据方舟 + UDataArkMode *string `required:"false"` + + // 业务组 默认:Default + Tag *string `required:"false"` + + // UDisk 类型: DataDisk(普通数据盘),SSDDataDisk(SSD数据盘),默认值(DataDisk) + DiskType *string `required:"false"` + + // 使用的代金券id + CouponId *string `required:"false"` +} + +// CreateUDiskResponse is response schema for CreateUDisk action +type CreateUDiskResponse struct { + response.CommonBase + + // UDisk实例Id + UDiskId []string +} + +// NewCreateUDiskRequest will create request of CreateUDisk action. +func (c *UDiskClient) NewCreateUDiskRequest() *CreateUDiskRequest { + req := &CreateUDiskRequest{} + + // setup request with client config + c.client.SetupRequest(req) + + // setup retryable with default retry policy (retry for non-create action and common error) + req.SetRetryable(false) + return req +} + +// CreateUDisk - 创建UDisk磁盘 +func (c *UDiskClient) CreateUDisk(req *CreateUDiskRequest) (*CreateUDiskResponse, error) { + var err error + var res CreateUDiskResponse + + err = c.client.InvokeAction("CreateUDisk", req, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/create_udisk_snapshot.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/create_udisk_snapshot.go new file mode 100644 index 0000000000..d6326886a6 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/create_udisk_snapshot.go @@ -0,0 +1,60 @@ +//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors. +//go:generate ucloud-gen-go-api UDisk CreateUDiskSnapshot + +package udisk + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// CreateUDiskSnapshotRequest is request schema for CreateUDiskSnapshot action +type CreateUDiskSnapshotRequest struct { + request.CommonBase + + // 可用区。参见 [可用区列表](../summary/regionlist.html) + Zone *string `required:"true"` + + // 快照的UDisk的Id + UDiskId *string `required:"true"` + + // 快照名称 + Name *string `required:"true"` + + // Year , Month, Dynamic 默认: Dynamic + ChargeType *string `required:"false"` + + // 购买时长 默认: 1 + Quantity *int `required:"false"` + + // 快照描述 + Comment *string `required:"false"` +} + +// CreateUDiskSnapshotResponse is response schema for CreateUDiskSnapshot action +type CreateUDiskSnapshotResponse struct { + response.CommonBase + + // 快照Id + SnapshotId []string +} + +// NewCreateUDiskSnapshotRequest will create request of CreateUDiskSnapshot action. +func (c *UDiskClient) NewCreateUDiskSnapshotRequest() *CreateUDiskSnapshotRequest { + req := &CreateUDiskSnapshotRequest{} + c.client.SetupRequest(req) + return req +} + +// CreateUDiskSnapshot - 创建snapshot快照 +func (c *UDiskClient) CreateUDiskSnapshot(req *CreateUDiskSnapshotRequest) (*CreateUDiskSnapshotResponse, error) { + var err error + var res CreateUDiskSnapshotResponse + + err = c.client.InvokeAction("CreateUDiskSnapshot", req, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/delete_udisk.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/delete_udisk.go new file mode 100644 index 0000000000..6e9775f9fb --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/delete_udisk.go @@ -0,0 +1,50 @@ +//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors. +//go:generate ucloud-gen-go-api UDisk DeleteUDisk + +package udisk + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// DeleteUDiskRequest is request schema for DeleteUDisk action +type DeleteUDiskRequest struct { + request.CommonBase + + // 可用区。参见 [可用区列表](../summary/regionlist.html) + Zone *string `required:"true"` + + // 要删除的UDisk的Id + UDiskId *string `required:"true"` +} + +// DeleteUDiskResponse is response schema for DeleteUDisk action +type DeleteUDiskResponse struct { + response.CommonBase +} + +// NewDeleteUDiskRequest will create request of DeleteUDisk action. +func (c *UDiskClient) NewDeleteUDiskRequest() *DeleteUDiskRequest { + req := &DeleteUDiskRequest{} + + // setup request with client config + c.client.SetupRequest(req) + + // setup retryable with default retry policy (retry for non-create action and common error) + req.SetRetryable(true) + return req +} + +// DeleteUDisk - 删除UDisk +func (c *UDiskClient) DeleteUDisk(req *DeleteUDiskRequest) (*DeleteUDiskResponse, error) { + var err error + var res DeleteUDiskResponse + + err = c.client.InvokeAction("DeleteUDisk", req, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/describe_udisk.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/describe_udisk.go new file mode 100644 index 0000000000..7a8f878df8 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/describe_udisk.go @@ -0,0 +1,65 @@ +//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors. +//go:generate ucloud-gen-go-api UDisk DescribeUDisk + +package udisk + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// DescribeUDiskRequest is request schema for DescribeUDisk action +type DescribeUDiskRequest struct { + request.CommonBase + + // 可用区。参见 [可用区列表](../summary/regionlist.html) + Zone *string `required:"false"` + + // UDisk Id(留空返回全部) + UDiskId *string `required:"false"` + + // 数据偏移量, 默认为0 + Offset *int `required:"false"` + + // 返回数据长度, 默认为20 + Limit *int `required:"false"` + + // 普通数据盘:DataDisk|普通系统盘:SystemDisk|SSD数据盘:SSDDataDisk; 为空拉取所有 + DiskType *string `required:"false"` +} + +// DescribeUDiskResponse is response schema for DescribeUDisk action +type DescribeUDiskResponse struct { + response.CommonBase + + // JSON 格式的UDisk数据列表, 每项参数可见下面 UDiskDataSet + DataSet []UDiskDataSet + + // 根据过滤条件得到的总数 + TotalCount int +} + +// NewDescribeUDiskRequest will create request of DescribeUDisk action. +func (c *UDiskClient) NewDescribeUDiskRequest() *DescribeUDiskRequest { + req := &DescribeUDiskRequest{} + + // setup request with client config + c.client.SetupRequest(req) + + // setup retryable with default retry policy (retry for non-create action and common error) + req.SetRetryable(true) + return req +} + +// DescribeUDisk - 获取UDisk实例 +func (c *UDiskClient) DescribeUDisk(req *DescribeUDiskRequest) (*DescribeUDiskResponse, error) { + var err error + var res DescribeUDiskResponse + + err = c.client.InvokeAction("DescribeUDisk", req, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/describe_udisk_price.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/describe_udisk_price.go new file mode 100644 index 0000000000..5959af93bd --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/describe_udisk_price.go @@ -0,0 +1,65 @@ +//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors. +//go:generate ucloud-gen-go-api UDisk DescribeUDiskPrice + +package udisk + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// DescribeUDiskPriceRequest is request schema for DescribeUDiskPrice action +type DescribeUDiskPriceRequest struct { + request.CommonBase + + // 可用区。参见 [可用区列表](../summary/regionlist.html) + Zone *string `required:"true"` + + // 购买UDisk大小,单位:GB,范围[1~1000] + Size *int `required:"true"` + + // Year, Month, Dynamic,Trial,默认: Dynamic 如果不指定,则一次性获取三种计费 + ChargeType *string `required:"false"` + + // 购买UDisk的时长,默认值为1 + Quantity *int `required:"false"` + + // 是否打开数据方舟, 打开"Yes",关闭"No", 默认关闭 + UDataArkMode *string `required:"false"` + + // UDisk 类型: DataDisk(普通数据盘),SSDDataDisk(SSD数据盘),默认值(DataDisk) + DiskType *string `required:"false"` +} + +// DescribeUDiskPriceResponse is response schema for DescribeUDiskPrice action +type DescribeUDiskPriceResponse struct { + response.CommonBase + + // 价格参数列表,具体说明见 UDiskPriceDataSet + DataSet []UDiskPriceDataSet +} + +// NewDescribeUDiskPriceRequest will create request of DescribeUDiskPrice action. +func (c *UDiskClient) NewDescribeUDiskPriceRequest() *DescribeUDiskPriceRequest { + req := &DescribeUDiskPriceRequest{} + + // setup request with client config + c.client.SetupRequest(req) + + // setup retryable with default retry policy (retry for non-create action and common error) + req.SetRetryable(true) + return req +} + +// DescribeUDiskPrice - 获取UDisk实例价格信息 +func (c *UDiskClient) DescribeUDiskPrice(req *DescribeUDiskPriceRequest) (*DescribeUDiskPriceResponse, error) { + var err error + var res DescribeUDiskPriceResponse + + err = c.client.InvokeAction("DescribeUDiskPrice", req, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/describe_udisk_upgrade_price.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/describe_udisk_upgrade_price.go new file mode 100644 index 0000000000..73e5f59240 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/describe_udisk_upgrade_price.go @@ -0,0 +1,62 @@ +//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors. +//go:generate ucloud-gen-go-api UDisk DescribeUDiskUpgradePrice + +package udisk + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// DescribeUDiskUpgradePriceRequest is request schema for DescribeUDiskUpgradePrice action +type DescribeUDiskUpgradePriceRequest struct { + request.CommonBase + + // 可用区。参见 [可用区列表](../summary/regionlist.html) + Zone *string `required:"true"` + + // 购买UDisk大小,单位:GB,范围[1~2000], 权限位控制可达8T,若需要请申请开通相关权限。 + Size *int `required:"true"` + + // 升级目标UDisk ID + SourceId *string `required:"true"` + + // 是否打开数据方舟, 打开"Yes",关闭"No", 默认关闭 + UDataArkMode *string `required:"true"` + + // 磁盘类型,SSDDataDisk:ssd数据盘,DataDisk:普通数据盘。默认为DataDisk + DiskType *string `required:"false"` +} + +// DescribeUDiskUpgradePriceResponse is response schema for DescribeUDiskUpgradePrice action +type DescribeUDiskUpgradePriceResponse struct { + response.CommonBase + + // 价格 + Price float64 +} + +// NewDescribeUDiskUpgradePriceRequest will create request of DescribeUDiskUpgradePrice action. +func (c *UDiskClient) NewDescribeUDiskUpgradePriceRequest() *DescribeUDiskUpgradePriceRequest { + req := &DescribeUDiskUpgradePriceRequest{} + + // setup request with client config + c.client.SetupRequest(req) + + // setup retryable with default retry policy (retry for non-create action and common error) + req.SetRetryable(true) + return req +} + +// DescribeUDiskUpgradePrice - 获取UDisk升级价格信息 +func (c *UDiskClient) DescribeUDiskUpgradePrice(req *DescribeUDiskUpgradePriceRequest) (*DescribeUDiskUpgradePriceResponse, error) { + var err error + var res DescribeUDiskUpgradePriceResponse + + err = c.client.InvokeAction("DescribeUDiskUpgradePrice", req, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/detach_udisk.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/detach_udisk.go new file mode 100644 index 0000000000..063f4419b2 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/detach_udisk.go @@ -0,0 +1,59 @@ +//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors. +//go:generate ucloud-gen-go-api UDisk DetachUDisk + +package udisk + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// DetachUDiskRequest is request schema for DetachUDisk action +type DetachUDiskRequest struct { + request.CommonBase + + // 可用区。参见 [可用区列表](../summary/regionlist.html) + Zone *string `required:"true"` + + // UHost实例ID + UHostId *string `required:"true"` + + // 需要卸载的UDisk实例ID + UDiskId *string `required:"true"` +} + +// DetachUDiskResponse is response schema for DetachUDisk action +type DetachUDiskResponse struct { + response.CommonBase + + // 卸载的UHost实例ID + UHostId string + + // 卸载的UDisk实例ID + UDiskId string +} + +// NewDetachUDiskRequest will create request of DetachUDisk action. +func (c *UDiskClient) NewDetachUDiskRequest() *DetachUDiskRequest { + req := &DetachUDiskRequest{} + + // setup request with client config + c.client.SetupRequest(req) + + // setup retryable with default retry policy (retry for non-create action and common error) + req.SetRetryable(true) + return req +} + +// DetachUDisk - 卸载某个已经挂载在指定UHost实例上的UDisk +func (c *UDiskClient) DetachUDisk(req *DetachUDiskRequest) (*DetachUDiskResponse, error) { + var err error + var res DetachUDiskResponse + + err = c.client.InvokeAction("DetachUDisk", req, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/doc.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/doc.go new file mode 100644 index 0000000000..2602d14c91 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/doc.go @@ -0,0 +1,11 @@ +/* + Package udisk include resources of ucloud disk product + + See also + + - API: https://docs.ucloud.cn/api/udisk-api/index + - Product: https://www.ucloud.cn/site/product/udisk.html + + for detail. +*/ +package udisk diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/enums.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/enums.go new file mode 100644 index 0000000000..e4f6b1f080 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/enums.go @@ -0,0 +1 @@ +package udisk diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/rename_udisk.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/rename_udisk.go new file mode 100644 index 0000000000..663f30a574 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/rename_udisk.go @@ -0,0 +1,53 @@ +//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors. +//go:generate ucloud-gen-go-api UDisk RenameUDisk + +package udisk + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// RenameUDiskRequest is request schema for RenameUDisk action +type RenameUDiskRequest struct { + request.CommonBase + + // 可用区。参见 [可用区列表](../summary/regionlist.html) + Zone *string `required:"true"` + + // 重命名的UDisk的Id + UDiskId *string `required:"true"` + + // 重命名UDisk的name + UDiskName *string `required:"true"` +} + +// RenameUDiskResponse is response schema for RenameUDisk action +type RenameUDiskResponse struct { + response.CommonBase +} + +// NewRenameUDiskRequest will create request of RenameUDisk action. +func (c *UDiskClient) NewRenameUDiskRequest() *RenameUDiskRequest { + req := &RenameUDiskRequest{} + + // setup request with client config + c.client.SetupRequest(req) + + // setup retryable with default retry policy (retry for non-create action and common error) + req.SetRetryable(true) + return req +} + +// RenameUDisk - 重命名UDisk +func (c *UDiskClient) RenameUDisk(req *RenameUDiskRequest) (*RenameUDiskResponse, error) { + var err error + var res RenameUDiskResponse + + err = c.client.InvokeAction("RenameUDisk", req, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/resize_udisk.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/resize_udisk.go new file mode 100644 index 0000000000..277a7678b2 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/resize_udisk.go @@ -0,0 +1,56 @@ +//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors. +//go:generate ucloud-gen-go-api UDisk ResizeUDisk + +package udisk + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// ResizeUDiskRequest is request schema for ResizeUDisk action +type ResizeUDiskRequest struct { + request.CommonBase + + // 可用区。参见 [可用区列表](../summary/regionlist.html) + Zone *string `required:"true"` + + // UDisk Id + UDiskId *string `required:"true"` + + // 调整后大小, 单位:GB, 范围[1~2000],权限位控制可达8000,若需要请申请开通相关权限。 + Size *int `required:"true"` + + // 使用的代金券id + CouponId *string `required:"false"` +} + +// ResizeUDiskResponse is response schema for ResizeUDisk action +type ResizeUDiskResponse struct { + response.CommonBase +} + +// NewResizeUDiskRequest will create request of ResizeUDisk action. +func (c *UDiskClient) NewResizeUDiskRequest() *ResizeUDiskRequest { + req := &ResizeUDiskRequest{} + + // setup request with client config + c.client.SetupRequest(req) + + // setup retryable with default retry policy (retry for non-create action and common error) + req.SetRetryable(true) + return req +} + +// ResizeUDisk - 调整UDisk容量 +func (c *UDiskClient) ResizeUDisk(req *ResizeUDiskRequest) (*ResizeUDiskResponse, error) { + var err error + var res ResizeUDiskResponse + + err = c.client.InvokeAction("ResizeUDisk", req, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/restore_udisk.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/restore_udisk.go new file mode 100644 index 0000000000..d4b5d670af --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/restore_udisk.go @@ -0,0 +1,56 @@ +//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors. +//go:generate ucloud-gen-go-api UDisk RestoreUDisk + +package udisk + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// RestoreUDiskRequest is request schema for RestoreUDisk action +type RestoreUDiskRequest struct { + request.CommonBase + + // 可用区。参见 [可用区列表](../summary/regionlist.html) + Zone *string `required:"true"` + + // 需要恢复的盘id + UDiskId *string `required:"true"` + + // 从指定的快照恢复 + SnapshotId *string `required:"false"` + + // 指定从方舟恢复的备份时间点 + SnapshotTime *int `required:"false"` +} + +// RestoreUDiskResponse is response schema for RestoreUDisk action +type RestoreUDiskResponse struct { + response.CommonBase +} + +// NewRestoreUDiskRequest will create request of RestoreUDisk action. +func (c *UDiskClient) NewRestoreUDiskRequest() *RestoreUDiskRequest { + req := &RestoreUDiskRequest{} + + // setup request with client config + c.client.SetupRequest(req) + + // setup retryable with default retry policy (retry for non-create action and common error) + req.SetRetryable(true) + return req +} + +// RestoreUDisk - 从备份恢复数据至UDisk +func (c *UDiskClient) RestoreUDisk(req *RestoreUDiskRequest) (*RestoreUDiskResponse, error) { + var err error + var res RestoreUDiskResponse + + err = c.client.InvokeAction("RestoreUDisk", req, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/set_udisk_udata_ark_mode.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/set_udisk_udata_ark_mode.go new file mode 100644 index 0000000000..0b34fb3a09 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/set_udisk_udata_ark_mode.go @@ -0,0 +1,53 @@ +//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors. +//go:generate ucloud-gen-go-api UDisk SetUDiskUDataArkMode + +package udisk + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// SetUDiskUDataArkModeRequest is request schema for SetUDiskUDataArkMode action +type SetUDiskUDataArkModeRequest struct { + request.CommonBase + + // 可用区。参见 [可用区列表](../summary/regionlist.html) + Zone *string `required:"true"` + + // 需要设置数据方舟的UDisk的Id + UDiskId *string `required:"true"` + + // 是否开启数据方舟,开启:"Yes", 不支持:"No" + UDataArkMode *string `required:"true"` +} + +// SetUDiskUDataArkModeResponse is response schema for SetUDiskUDataArkMode action +type SetUDiskUDataArkModeResponse struct { + response.CommonBase +} + +// NewSetUDiskUDataArkModeRequest will create request of SetUDiskUDataArkMode action. +func (c *UDiskClient) NewSetUDiskUDataArkModeRequest() *SetUDiskUDataArkModeRequest { + req := &SetUDiskUDataArkModeRequest{} + + // setup request with client config + c.client.SetupRequest(req) + + // setup retryable with default retry policy (retry for non-create action and common error) + req.SetRetryable(true) + return req +} + +// SetUDiskUDataArkMode - 设置UDisk数据方舟的状态 +func (c *UDiskClient) SetUDiskUDataArkMode(req *SetUDiskUDataArkModeRequest) (*SetUDiskUDataArkModeResponse, error) { + var err error + var res SetUDiskUDataArkModeResponse + + err = c.client.InvokeAction("SetUDiskUDataArkMode", req, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/types_udisk_data_set.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/types_udisk_data_set.go new file mode 100644 index 0000000000..b102f20d25 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/types_udisk_data_set.go @@ -0,0 +1,64 @@ +package udisk + +/* + UDiskDataSet - DescribeUDisk + + this model is auto created by ucloud code generater for open api, + you can also see https://docs.ucloud.cn for detail. +*/ +type UDiskDataSet struct { + + // UDisk实例Id + UDiskId string + + // 实例名称 + Name string + + // 容量单位GB + Size int + + // 状态:Available(可用),Attaching(挂载中), InUse(已挂载), Detaching(卸载中), Initializating(分配中), Failed(创建失败),Cloning(克隆中),Restoring(恢复中),RestoreFailed(恢复失败), + Status string + + // 创建时间 + CreateTime int + + // 过期时间 + ExpiredTime int + + // 挂载的UHost的Id + UHostId string + + // 挂载的UHost的Name + UHostName string + + // 挂载的UHost的IP + UHostIP string + + // 挂载的设备名称 + DeviceName string + + // Year,Month,Dynamic,Trial + ChargeType string + + // 业务组名称 + Tag string + + // 资源是否过期,过期:"Yes", 未过期:"No" + IsExpire string + + // 是否支持数据方舟,支持:"2.0", 不支持:"1.0" + Version string + + // 是否开启数据方舟,开启:"Yes", 不支持:"No" + UDataArkMode string + + // 该盘快照个数 + SnapshotCount int + + // 该盘快照上限 + SnapshotLimit int + + // 云硬盘类型: 普通数据盘:DataDisk,普通系统盘:SystemDisk,SSD数据盘:SSDDataDisk + DiskType string +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/types_udisk_price_data_set.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/types_udisk_price_data_set.go new file mode 100644 index 0000000000..cf9273e4bc --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/udisk/types_udisk_price_data_set.go @@ -0,0 +1,19 @@ +package udisk + +/* + UDiskPriceDataSet - DescribeUDiskPrice + + this model is auto created by ucloud code generater for open api, + you can also see https://docs.ucloud.cn for detail. +*/ +type UDiskPriceDataSet struct { + + // Year, Month, Dynamic,Trial + ChargeType string + + // 价格 (单位: 分) + Price float64 + + // "UDataArk","UDisk" + ChargeName string +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 61a34bc176..8f17e0e52e 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -56,6 +56,12 @@ "revision": "ae92fcdf66a5f34c27d8ae1f81b10033648275f4", "revisionTime": "2018-10-24T10:31:01Z" }, + { + "checksumSHA1": "/Q1QxXVSDx6egkzPBI4PxxBgpUI=", + "path": "github.com/ucloud/ucloud-sdk-go/services/udisk", + "revision": "ae92fcdf66a5f34c27d8ae1f81b10033648275f4", + "revisionTime": "2018-10-24T10:31:01Z" + }, { "checksumSHA1": "mT4ix7tzri+ORhkQDUvNvRuGK4Y=", "path": "github.com/ucloud/ucloud-sdk-go/services/uhost", From b14c4e84e6a8a3775327ec6d76d62a4901a1b1ba Mon Sep 17 00:00:00 2001 From: lixiaojun Date: Mon, 5 Nov 2018 16:22:07 +0800 Subject: [PATCH 3/4] optimization and bugfix --- base/util.go | 8 + cmd/disk.go | 301 ++++++++----- cmd/eip.go | 175 +++++--- cmd/root.go | 12 +- cmd/uhost.go | 405 ++++++++++++------ ux/spinner.go | 9 +- .../uhost/terminate_uhost_instance.go | 4 +- 7 files changed, 613 insertions(+), 301 deletions(-) diff --git a/base/util.go b/base/util.go index 03078ead6e..3539fdb9ab 100644 --- a/base/util.go +++ b/base/util.go @@ -318,3 +318,11 @@ func Poll(describeFunc func(string, string, string, string) (interface{}, error) return done } } + +//PickResourceID uhost-xxx/uhost-name => uhost-xxx +func PickResourceID(str string) string { + if strings.Index(str, "/") > -1 { + return strings.SplitN(str, "/", 2)[0] + } + return str +} diff --git a/cmd/disk.go b/cmd/disk.go index 19ff0f5e5d..96de339b2e 100644 --- a/cmd/disk.go +++ b/cmd/disk.go @@ -20,6 +20,7 @@ import ( "github.com/spf13/cobra" + "github.com/ucloud/ucloud-sdk-go/services/udisk" sdk "github.com/ucloud/ucloud-sdk-go/ucloud" "github.com/ucloud/ucloud-cli/base" @@ -30,9 +31,9 @@ import ( //NewCmdDisk ucloud disk func NewCmdDisk() *cobra.Command { cmd := &cobra.Command{ - Use: "disk", - Short: "Read and manipulate ucloud disks", - Long: "Read and manipulate ucloud disks", + Use: "udisk", + Short: "Read and manipulate udisk instances", + Long: "Read and manipulate udisk instances", } cmd.AddCommand(NewCmdDiskCreate()) cmd.AddCommand(NewCmdDiskList()) @@ -44,15 +45,21 @@ func NewCmdDisk() *cobra.Command { return cmd } -//NewCmdDiskCreate ucloud disk create +//NewCmdDiskCreate ucloud udisk create func NewCmdDiskCreate() *cobra.Command { + var async *bool + var count *int req := base.BizClient.NewCreateUDiskRequest() enableDataArk := sdk.String("false") cmd := &cobra.Command{ Use: "create", - Short: "Create ucloud disk", - Long: "Create ucloud disk", + Short: "Create udisk instance", + Long: "Create udisk instance", Run: func(cmd *cobra.Command, args []string) { + if *count > 10 || *count < 1 { + base.Cxt.Printf("Error, count should be between 1 and 10\n") + return + } if *enableDataArk == "true" { req.UDataArkMode = sdk.String("Yes") } else { @@ -64,39 +71,46 @@ func NewCmdDiskCreate() *cobra.Command { } else if *req.DiskType == "SSD" { *req.DiskType = "SSDDataDisk" } - - resp, err := base.BizClient.CreateUDisk(req) - if err != nil { - base.HandleError(err) - return - } - if count := len(resp.UDiskId); count == 1 { - text := fmt.Sprintf("udisk:%v is initializating", resp.UDiskId) - pollDisk(resp.UDiskId[0], *req.ProjectId, *req.Region, *req.Zone, text, []string{status.DISK_AVAILABLE, status.DISK_FAILED}) - } else if count > 1 { - base.Cxt.Printf("udisk:%v created\n", resp.UDiskId) - } else { - base.Cxt.PrintErr(fmt.Errorf("none disk created")) + for i := 0; i < *count; i++ { + resp, err := base.BizClient.CreateUDisk(req) + if err != nil { + base.HandleError(err) + return + } + if count := len(resp.UDiskId); count == 1 { + text := fmt.Sprintf("udisk:%v is initializing", resp.UDiskId) + if *async { + base.Cxt.Println(text) + } else { + pollDisk(resp.UDiskId[0], *req.ProjectId, *req.Region, *req.Zone, text, []string{status.DISK_AVAILABLE, status.DISK_FAILED}) + } + } else if count > 1 { + base.Cxt.Printf("udisk:%v created\n", resp.UDiskId) + } else { + base.Cxt.PrintErr(fmt.Errorf("none udisk created")) + } } }, } flags := cmd.Flags() flags.SortFlags = false + req.Name = flags.String("name", "", "Required. Name of the udisk to create") + req.Size = flags.Int("size-gb", 10, "Required. Size of the udisk to create. Unit:GB. Normal udisk [1,8000]; SSD udisk [1,4000] ") req.ProjectId = flags.String("project-id", base.ConfigInstance.ProjectID, "Optional. Assign project-id") req.Region = flags.String("region", base.ConfigInstance.Region, "Optional. Assign region") req.Zone = flags.String("zone", base.ConfigInstance.Zone, "Optional. Assign availability zone") - req.Name = flags.String("name", "", "Required. Name of the disk to create") - req.Size = flags.Int("size-gb", 10, "Required. Size of the disk to create. Unit:GB. Normal disk [1,8000]; SSD disk [1,4000] ") req.ChargeType = flags.String("charge-type", "Dynamic", "Optional.'Year',pay yearly;'Month',pay monthly;'Dynamic', pay hourly") req.Quantity = flags.Int("quantity", 1, "Optional. The duration of the instance. N years/months.") - enableDataArk = flags.String("enable-data-ark", "false", "Optional. DataArk supports real-time backup, which can restore the disk back to any moment within the last 12 hours.") + enableDataArk = flags.String("enable-data-ark", "false", "Optional. DataArk supports real-time backup, which can restore the udisk back to any moment within the last 12 hours.") req.Tag = flags.String("group", "Default", "Optional. Business group") - req.DiskType = flags.String("disk-type", "Oridinary", "Optional. 'Ordinary' or 'SSD'") + req.DiskType = flags.String("udisk-type", "Oridinary", "Optional. 'Ordinary' or 'SSD'") req.CouponId = flags.String("coupon-id", "", "Optional. Coupon ID, The Coupon can deduct part of the payment.See https://accountv2.ucloud.cn") + async = flags.Bool("async", false, "Optional. Do not wait for the long-running operation to finish.") + count = flags.Int("count", 1, "Optional. The count of udisk to create. Range [1,10]") flags.SetFlagValues("charge-type", "Month", "Year", "Dynamic", "Trial") flags.SetFlagValues("enable-data-ark", "true", "false") - flags.SetFlagValues("disk-type", "Oridinary", "SSD") + flags.SetFlagValues("udisk-type", "Oridinary", "SSD") cmd.MarkFlagRequired("size-gb") cmd.MarkFlagRequired("name") @@ -133,8 +147,8 @@ func NewCmdDiskList() *cobra.Command { } cmd := &cobra.Command{ Use: "list", - Short: "List ucloud disk", - Long: "List ucloud disk", + Short: "List udisk instance", + Long: "List udisk instance", Run: func(cmd *cobra.Command, args []string) { for key, val := range typeMap { if *req.DiskType == val { @@ -155,15 +169,22 @@ func NewCmdDiskList() *cobra.Command { Size: fmt.Sprintf("%dGB", disk.Size), Type: typeMap[disk.DiskType], EnableDataArk: arkModeMap[disk.UDataArkMode], - MountUHost: disk.UHostIP, - MountPoint: fmt.Sprintf("%s", disk.DeviceName), + MountUHost: fmt.Sprintf("%s/%s", disk.UHostName, disk.UHostIP), + MountPoint: disk.DeviceName, State: disk.Status, CreationTime: base.FormatDate(disk.CreateTime), ExpirationTime: base.FormatDate(disk.ExpiredTime), } + if disk.UHostIP == "" { + row.MountUHost = "" + } list = append(list, row) } - base.PrintTableS(list) + if global.json { + base.PrintJSON(list) + } else { + base.PrintTableS(list) + } }, } flags := cmd.Flags() @@ -171,126 +192,196 @@ func NewCmdDiskList() *cobra.Command { req.ProjectId = flags.String("project-id", base.ConfigInstance.ProjectID, "Optional. Assign project-id") req.Region = flags.String("region", base.ConfigInstance.Region, "Optional. Assign region") req.Zone = flags.String("zone", base.ConfigInstance.Zone, "Optional. Assign availability zone") - req.UDiskId = flags.String("resource-id", "", "Optional. Resource ID of the disk to search") - req.DiskType = flags.String("disk-type", "", "Optional. Optional. Type of the disk to search. 'Oridinary-Data-Disk','Oridinary-System-Disk' or 'SSD-Data-Disk'") + req.UDiskId = flags.String("resource-id", "", "Optional. Resource ID of the udisk to search") + req.DiskType = flags.String("udisk-type", "", "Optional. Optional. Type of the udisk to search. 'Oridinary-Data-Disk','Oridinary-System-Disk' or 'SSD-Data-Disk'") req.Offset = cmd.Flags().Int("offset", 0, "Optional. Offset") req.Limit = cmd.Flags().Int("limit", 50, "Optional. Limit") - flags.SetFlagValues("disk-type", "Oridinary-Data-Disk", "Oridinary-System-Disk", "SSD-Data-Disk") + flags.SetFlagValues("udisk-type", "Oridinary-Data-Disk", "Oridinary-System-Disk", "SSD-Data-Disk") return cmd } //NewCmdDiskAttach ucloud disk attach func NewCmdDiskAttach() *cobra.Command { + var async *bool + var udiskIDs *[]string + req := base.BizClient.NewAttachUDiskRequest() cmd := &cobra.Command{ - Use: "attach", - Short: "Attach a disk to an uhost instance", - Long: "Attach a disk to an uhost instance", + Use: "attach", + Short: "Attach udisk instances to an uhost", + Long: "Attach udisk instances to an uhost", + Example: "ucloud udisk attach --uhost-id uhost-xxxx --udisk-id bs-xxx1,bs-xxx2", Run: func(cmd *cobra.Command, args []string) { - resp, err := base.BizClient.AttachUDisk(req) - if err != nil { - base.HandleError(err) - return + for _, id := range *udiskIDs { + id = base.PickResourceID(id) + req.UDiskId = &id + *req.UHostId = base.PickResourceID(*req.UHostId) + resp, err := base.BizClient.AttachUDisk(req) + if err != nil { + base.HandleError(err) + return + } + text := fmt.Sprintf("udisk[%s] is attaching to uhost uhost[%s]", *req.UDiskId, *req.UHostId) + if *async { + base.Cxt.Println(text) + } else { + pollDisk(resp.UDiskId, *req.ProjectId, *req.Region, *req.Zone, text, []string{status.DISK_INUSE, status.DISK_FAILED}) + } } - text := fmt.Sprintf("udisk[%s] is attaching to uhost uhost[%s]", *req.UDiskId, *req.UHostId) - pollDisk(resp.UDiskId, *req.ProjectId, *req.Region, *req.Zone, text, []string{status.DISK_INUSE, status.DISK_FAILED}) }, } flags := cmd.Flags() flags.SortFlags = false + req.UHostId = flags.String("uhost-id", "", "Required. Resource ID of the uhost instance which you want to attach the disk") + udiskIDs = flags.StringSlice("udisk-id", nil, "Required. Resource ID of the udisk instances to attach") req.ProjectId = flags.String("project-id", base.ConfigInstance.ProjectID, "Optional. Assign project-id") req.Region = flags.String("region", base.ConfigInstance.Region, "Optional. Assign region") req.Zone = flags.String("zone", base.ConfigInstance.Zone, "Optional. Assign availability zone") - req.UHostId = flags.String("uhost-id", "", "Resource ID of the uhost instance which you want to attach the disk") - req.UDiskId = flags.String("disk-id", "", "Resource ID of the udisk instance to attach") + async = flags.Bool("async", false, "Optional. Do not wait for the long-running operation to finish.") + + flags.SetFlagValuesFunc("udisk-id", func() []string { + return getDiskList([]string{status.DISK_AVAILABLE}, *req.ProjectId, *req.Region, *req.Zone) + }) + flags.SetFlagValuesFunc("uhost-id", func() []string { + return getUhostList([]string{status.HOST_RUNNING, status.HOST_STOPPED}, *req.ProjectId, *req.Region, *req.Zone) + }) cmd.MarkFlagRequired("uhost-id") - cmd.MarkFlagRequired("disk-id") + cmd.MarkFlagRequired("udisk-id") return cmd } -//NewCmdDiskDetach ucloud disk detach +//NewCmdDiskDetach ucloud udisk detach func NewCmdDiskDetach() *cobra.Command { + var async, yes *bool + var udiskIDs *[]string req := base.BizClient.NewDetachUDiskRequest() cmd := &cobra.Command{ Use: "detach", - Short: "Detach an ucloud disk from uhost", - Long: "Detach an ucloud disk from uhost", + Short: "Detach udisk instances from an uhost", + Long: "Detach udisk instances from an uhost", Run: func(cmd *cobra.Command, args []string) { - resp, err := base.BizClient.DetachUDisk(req) - if err != nil { - base.HandleError(err) - return + text := `Please confirm that you have already unmounted file system corresponding to this hard drive,(See "https://docs.ucloud.cn/storage_cdn/udisk/userguide/umount" for help), otherwise it will cause file system damage and UHost cannot be normally shut down. Sure to detach?` + if !*yes { + sure, err := ux.Prompt(text) + if err != nil { + base.Cxt.PrintErr(err) + return + } + if !sure { + return + } + } + for _, id := range *udiskIDs { + id = base.PickResourceID(id) + any, err := describeUdiskByID(id, *req.ProjectId, *req.Region, *req.Zone) + if err != nil { + base.HandleError(err) + continue + } + if any == nil { + base.Cxt.PrintErr(fmt.Errorf("udisk[%v] is not exist", any)) + continue + } + ins, ok := any.(*udisk.UDiskDataSet) + if !ok { + base.Cxt.PrintErr(fmt.Errorf("%#v convert to udisk failed", any)) + continue + } + req.UHostId = &ins.UHostId + req.UDiskId = &id + *req.UHostId = base.PickResourceID(*req.UHostId) + resp, err := base.BizClient.DetachUDisk(req) + if err != nil { + base.HandleError(err) + continue + } + text := fmt.Sprintf("udisk[%s] is detaching from uhost[%s]", resp.UDiskId, resp.UHostId) + if *async { + base.Cxt.Println(text) + } else { + pollDisk(resp.UDiskId, *req.ProjectId, *req.Region, *req.Zone, text, []string{status.DISK_AVAILABLE, status.DISK_FAILED}) + } } - text := fmt.Sprintf("udisk[%s] is detaching from uhost[%s]", resp.UDiskId, resp.UHostId) - pollDisk(resp.UDiskId, *req.ProjectId, *req.Region, *req.Zone, text, []string{status.DISK_AVAILABLE, status.DISK_FAILED}) }, } flags := cmd.Flags() flags.SortFlags = false + udiskIDs = flags.StringSlice("udisk-id", nil, "Required. Resource ID of the udisk instances to detach") req.ProjectId = flags.String("project-id", base.ConfigInstance.ProjectID, "Optional. Assign project-id") req.Region = flags.String("region", base.ConfigInstance.Region, "Optional. Assign region") req.Zone = flags.String("zone", base.ConfigInstance.Zone, "Optional. Assign availability zone") - req.UHostId = flags.String("uhost-id", "", "Resource ID of the uhost instance, from which you want to detach the disk") - req.UDiskId = flags.String("disk-id", "", "Resource ID of the udisk instance to detach") + async = flags.Bool("async", false, "Optional. Do not wait for the long-running operation to finish.") + yes = flags.BoolP("yes", "y", false, "Optional. Do not prompt for confirmation.") - cmd.MarkFlagRequired("uhost-id") - cmd.MarkFlagRequired("disk-id") + flags.SetFlagValuesFunc("udisk-id", func() []string { + return getDiskList([]string{status.DISK_INUSE}, *req.ProjectId, *req.Region, *req.Zone) + }) + + cmd.MarkFlagRequired("udisk-id") return cmd } -//NewCmdDiskDelete ucloud disk delete +//NewCmdDiskDelete ucloud udisk delete func NewCmdDiskDelete() *cobra.Command { + var yes *bool + var udiskIDs *[]string req := base.BizClient.NewDeleteUDiskRequest() cmd := &cobra.Command{ Use: "delete", - Short: "Delete an ucloud disk", - Long: "Delete an ucloud disk", + Short: "Delete udisk instances", + Long: "Delete udisk instances", Run: func(cmd *cobra.Command, args []string) { - if strings.Index(*req.UDiskId, "/") > -1 { - *req.UDiskId = strings.SplitN(*req.UDiskId, "/", 2)[0] - } - sure, err := ux.Prompt(fmt.Sprintf("Are you sure to delete disk[%s]?", *req.UDiskId)) - if err != nil { - base.Cxt.PrintErr(err) - return - } - if !sure { - return + if !*yes { + sure, err := ux.Prompt(fmt.Sprintf("Are you sure to delete udisk(s)?")) + if err != nil { + base.Cxt.PrintErr(err) + return + } + if !sure { + return + } } - _, err = base.BizClient.DeleteUDisk(req) - if err != nil { - base.HandleError(err) - return + for _, id := range *udiskIDs { + id := base.PickResourceID(id) + req.UDiskId = &id + _, err := base.BizClient.DeleteUDisk(req) + if err != nil { + base.HandleError(err) + continue + } else { + base.Cxt.Printf("udisk[%s] deleted\n", *req.UDiskId) + } } - base.Cxt.Printf("udisk[%s] deleted\n", *req.UDiskId) }, } flags := cmd.Flags() flags.SortFlags = false + udiskIDs = flags.StringSlice("udisk-id", nil, "Required. The Resource ID of udisks to delete") req.ProjectId = flags.String("project-id", base.ConfigInstance.ProjectID, "Optional. Assign project-id") req.Region = flags.String("region", base.ConfigInstance.Region, "Optional. Assign region") req.Zone = flags.String("zone", base.ConfigInstance.Zone, "Optional. Assign availability zone") - req.UDiskId = flags.String("resource-id", "", "Required. The Resource ID of a disk to delete") + yes = flags.BoolP("yes", "y", false, "Optional. Do not prompt for confirmation.") - flags.SetFlagValuesFunc("resource-id", func() []string { + flags.SetFlagValuesFunc("udisk-id", func() []string { return getDiskList([]string{status.DISK_AVAILABLE, status.DISK_FAILED}, *req.ProjectId, *req.Region, *req.Zone) }) - cmd.MarkFlagRequired("resource-id") + cmd.MarkFlagRequired("udisk-id") return cmd } //NewCmdDiskClone ucloud disk clone func NewCmdDiskClone() *cobra.Command { + var async *bool req := base.BizClient.NewCloneUDiskRequest() enableDataArk := sdk.String("false") cmd := &cobra.Command{ Use: "clone", - Short: "Clone an ucloud disk", - Long: "Clone an ucloud disk", + Short: "Clone an udisk", + Long: "Clone an udisk", Run: func(cmd *cobra.Command, args []string) { if *enableDataArk == "true" { req.UDataArkMode = sdk.String("Yes") @@ -306,24 +397,29 @@ func NewCmdDiskClone() *cobra.Command { return } if len(resp.UDiskId) == 1 { - pollText := fmt.Sprintf("cloned disk:[%s] is initializating", resp.UDiskId[0]) - pollDisk(resp.UDiskId[0], *req.ProjectId, *req.Region, *req.Zone, pollText, []string{status.DISK_AVAILABLE, status.DISK_FAILED}) + text := fmt.Sprintf("cloned udisk:[%s] is initializing", resp.UDiskId[0]) + if *async { + base.Cxt.Println(text) + } else { + pollDisk(resp.UDiskId[0], *req.ProjectId, *req.Region, *req.Zone, text, []string{status.DISK_AVAILABLE, status.DISK_FAILED}) + } } else { - base.Cxt.Printf("disk[%v] cloned", resp.UDiskId) + base.Cxt.Printf("udisk[%v] cloned", resp.UDiskId) } }, } flags := cmd.Flags() flags.SortFlags = false - req.SourceId = flags.String("source-id", "", "Required. Resource ID of parent disk") - req.Name = flags.String("name", "", "Required. Name of new disk") + req.SourceId = flags.String("source-id", "", "Required. Resource ID of parent udisk") + req.Name = flags.String("name", "", "Required. Name of new udisk") req.ProjectId = flags.String("project-id", base.ConfigInstance.ProjectID, "Optional. Assign project-id") req.Region = flags.String("region", base.ConfigInstance.Region, "Optional. Assign region") req.Zone = flags.String("zone", base.ConfigInstance.Zone, "Optional. Assign availability zone") req.ChargeType = flags.String("charge-type", "Month", "Optional.'Year',pay yearly;'Month',pay monthly;'Dynamic', pay hourly") req.Quantity = flags.Int("quantity", 1, "Optional. The duration of the instance. N years/months.") - enableDataArk = flags.String("enable-data-ark", "false", "Optional. DataArk supports real-time backup, which can restore the disk back to any moment within the last 12 hours.") + enableDataArk = flags.String("enable-data-ark", "false", "Optional. DataArk supports real-time backup, which can restore the udisk back to any moment within the last 12 hours.") req.CouponId = flags.String("coupon-id", "", "Optional. Coupon ID, The Coupon can deduct part of the payment,see https://accountv2.ucloud.cn") + async = flags.Bool("async", false, "Optional. Do not wait for the long-running operation to finish.") flags.SetFlagValues("charge-type", "Month", "Year", "Dynamic", "Trial") flags.SetFlagValues("enable-data-ark", "true", "false") @@ -338,42 +434,45 @@ func NewCmdDiskClone() *cobra.Command { return cmd } -//NewCmdDiskExpand ucloud disk expand +//NewCmdDiskExpand ucloud udisk expand func NewCmdDiskExpand() *cobra.Command { + var udiskIDs *[]string req := base.BizClient.NewResizeUDiskRequest() cmd := &cobra.Command{ Use: "expand", - Short: "Expand disk size", - Long: "Expand disk size", + Short: "Expand udisk size", + Long: "Expand udisk size", Run: func(cmd *cobra.Command, args []string) { - if strings.Index(*req.UDiskId, "/") > -1 { - *req.UDiskId = strings.SplitN(*req.UDiskId, "/", 2)[0] - } if *req.Size > 8000 || *req.Size < 1 { base.Cxt.Println("size-gb should be between 1 and 8000") - } - _, err := base.BizClient.ResizeUDisk(req) - if err != nil { - base.HandleError(err) return } - base.Cxt.Printf("disk:[%s] expanded to %d GB\n", *req.UDiskId, *req.Size) + for _, id := range *udiskIDs { + id = base.PickResourceID(id) + req.UDiskId = &id + _, err := base.BizClient.ResizeUDisk(req) + if err != nil { + base.HandleError(err) + return + } + base.Cxt.Printf("udisk:[%s] expanded to %d GB\n", *req.UDiskId, *req.Size) + } }, } flags := cmd.Flags() flags.SortFlags = false - req.UDiskId = flags.String("disk-id", "", "Required. Resource ID of the disk to expand") - req.Size = flags.Int("size-gb", 0, "Required. Size of the disk after expanded. Unit: GB. Range [1,8000]") + udiskIDs = flags.StringSlice("udisk-id", nil, "Required. Resource ID of the udisks to expand") + req.Size = flags.Int("size-gb", 0, "Required. Size of the udisk after expanded. Unit: GB. Range [1,8000]") req.ProjectId = flags.String("project-id", base.ConfigInstance.ProjectID, "Optional. Assign project-id") req.Region = flags.String("region", base.ConfigInstance.Region, "Optional. Assign region") req.Zone = flags.String("zone", base.ConfigInstance.Zone, "Optional. Assign availability zone") req.CouponId = flags.String("coupon-id", "", "Optional. Coupon ID, The Coupon can deduct part of the payment,see https://accountv2.ucloud.cn") - flags.SetFlagValuesFunc("disk-id", func() []string { + flags.SetFlagValuesFunc("udisk-id", func() []string { return getDiskList([]string{status.DISK_AVAILABLE}, *req.ProjectId, *req.Region, *req.Zone) }) - cmd.MarkFlagRequired("disk-id") + cmd.MarkFlagRequired("udisk-id") cmd.MarkFlagRequired("size-gb") return cmd diff --git a/cmd/eip.go b/cmd/eip.go index 4d5106d4e2..f397cd3e0d 100644 --- a/cmd/eip.go +++ b/cmd/eip.go @@ -16,9 +16,12 @@ package cmd import ( "fmt" + "net" "strconv" "time" + "github.com/ucloud/ucloud-sdk-go/services/unet" + "github.com/spf13/cobra" sdk "github.com/ucloud/ucloud-sdk-go/ucloud" @@ -55,52 +58,109 @@ type EIPRow struct { ExpirationTime string } -//NewCmdEIPList ucloud eip ls +//NewCmdEIPList ucloud eip list func NewCmdEIPList() *cobra.Command { req := BizClient.NewDescribeEIPRequest() + fetchAll := sdk.Bool(false) cmd := &cobra.Command{ Use: "list", Short: "List all EIP instances", Long: `List all EIP instances`, - Example: "ucloud eip ls", + Example: "ucloud eip list", Run: func(cmd *cobra.Command, args []string) { - resp, err := BizClient.DescribeEIP(req) - if err != nil { - HandleError(err) + var eipList []unet.UnetEIPSet + if *fetchAll == true { + list, err := fetchAllEip(*req.ProjectId, *req.Region) + if err != nil { + HandleError(err) + return + } + eipList = list } else { - if global.json { - PrintJSON(resp.EIPSet) - } else { - list := make([]EIPRow, 0) - for _, eip := range resp.EIPSet { - row := EIPRow{} - row.Name = eip.Name - for _, ip := range eip.EIPAddr { - row.IP += ip.IP + " " + ip.OperatorName + " " - } - row.ResourceID = eip.EIPId - row.Group = eip.Tag - row.Billing = eip.PayMode - row.Bandwidth = strconv.Itoa(eip.Bandwidth) + "Mb" - if eip.Resource.ResourceId != "" { - row.BindResource = fmt.Sprintf("%s|%s(%s)", eip.Resource.ResourceName, eip.Resource.ResourceId, eip.Resource.ResourceType) - } - row.Status = eip.Status - row.ExpirationTime = time.Unix(int64(eip.ExpireTime), 0).Format("2006-01-02") - list = append(list, row) + resp, err := BizClient.DescribeEIP(req) + if err != nil { + HandleError(err) + return + } + eipList = resp.EIPSet + } + + if global.json { + PrintJSON(eipList) + } else { + list := make([]EIPRow, 0) + for _, eip := range eipList { + row := EIPRow{} + row.Name = eip.Name + for _, ip := range eip.EIPAddr { + row.IP += ip.IP + " " + ip.OperatorName + " " + } + row.ResourceID = eip.EIPId + row.Group = eip.Tag + row.Billing = eip.PayMode + row.Bandwidth = strconv.Itoa(eip.Bandwidth) + "Mb" + if eip.Resource.ResourceId != "" { + row.BindResource = fmt.Sprintf("%s|%s(%s)", eip.Resource.ResourceName, eip.Resource.ResourceId, eip.Resource.ResourceType) } - PrintTable(list, []string{"Name", "IP", "ResourceID", "Group", "Billing", "Bandwidth", "BindResource", "Status", "ExpirationTime"}) + row.Status = eip.Status + row.ExpirationTime = time.Unix(int64(eip.ExpireTime), 0).Format("2006-01-02") + list = append(list, row) } + PrintTable(list, []string{"Name", "IP", "ResourceID", "Group", "Billing", "Bandwidth", "BindResource", "Status", "ExpirationTime"}) } }, } + req.ProjectId = cmd.Flags().String("project-id", ConfigInstance.ProjectID, "Assign project-id") req.Region = cmd.Flags().String("region", ConfigInstance.Region, "Assign region") + req.Offset = cmd.Flags().Int("offset", 0, "Optional. Offset default 0") + req.Limit = cmd.Flags().Int("limit", 50, "Optional. Limit default 50, max value 100") + fetchAll = cmd.Flags().Bool("list-all", false, "List all eip") + cmd.Flags().SetFlagValues("list-all", "true", "false") + return cmd } +func getEIPIDbyIP(ip net.IP, projectID, region string) (string, error) { + eipList, err := fetchAllEip(projectID, region) + if err != nil { + return "", err + } + for _, eip := range eipList { + for _, addr := range eip.EIPAddr { + if addr.IP == ip.String() { + return eip.EIPId, nil + } + } + } + return "", fmt.Errorf("IP[%s] not exist", ip.String()) +} + +func fetchAllEip(projectID, region string) ([]unet.UnetEIPSet, error) { + req := BizClient.NewDescribeEIPRequest() + list := []unet.UnetEIPSet{} + req.ProjectId = sdk.String(projectID) + req.Region = sdk.String(region) + for offset, step := 0, 100; ; offset += step { + req.Offset = &offset + req.Limit = &step + resp, err := BizClient.DescribeEIP(req) + if err != nil { + return nil, err + } + for i, size := 0, len(resp.EIPSet); i < size; i++ { + list = append(list, resp.EIPSet[i]) + } + if resp.TotalCount <= offset+step { + break + } + } + return list, nil +} + //NewCmdEIPAllocate ucloud eip allocate func NewCmdEIPAllocate() *cobra.Command { + var count *int var req = BizClient.NewAllocateEIPRequest() var cmd = &cobra.Command{ Use: "allocate", @@ -111,37 +171,41 @@ func NewCmdEIPAllocate() *cobra.Command { if *req.OperatorName == "BGP" { *req.OperatorName = "Bgp" } - resp, err := BizClient.AllocateEIP(req) - if err != nil { - HandleError(err) - } else { - for _, eip := range resp.EIPSet { - Cxt.Printf("allocate EIP[%s] ", eip.EIPId) - for _, ip := range eip.EIPAddr { - Cxt.Printf("IP:%s Line:%s \n", ip.IP, ip.OperatorName) + for i := 0; i < *count; i++ { + resp, err := BizClient.AllocateEIP(req) + if err != nil { + HandleError(err) + } else { + for _, eip := range resp.EIPSet { + Cxt.Printf("allocate EIP[%s] ", eip.EIPId) + for _, ip := range eip.EIPAddr { + Cxt.Printf("IP:%s Line:%s \n", ip.IP, ip.OperatorName) + } } } } }, } cmd.Flags().SortFlags = false - req.ProjectId = cmd.Flags().String("project-id", ConfigInstance.ProjectID, "Assign project-id") - req.Region = cmd.Flags().String("region", ConfigInstance.Region, "Assign region") req.OperatorName = cmd.Flags().String("line", "", "Required. 'BGP' or 'International'. 'BGP' could be set in China mainland regions, such as cn-bj2 etc. 'International' could be set in the regions beyond mainland, such as hk, tw-kh, us-ws etc.") - req.Bandwidth = cmd.Flags().Int("bandwidth", 0, "Required. Bandwidth(Unit:Mbps).The range of value related to network charge mode. By traffic [1, 200]; by bandwidth [1,800] (Unit: Mbps); it could be 0 if the eip belong to the shared bandwidth") + req.Bandwidth = cmd.Flags().Int("bandwidth-mb", 0, "Required. Bandwidth(Unit:Mbps).The range of value related to network charge mode. By traffic [1, 200]; by bandwidth [1,800] (Unit: Mbps); it could be 0 if the eip belong to the shared bandwidth") + req.ProjectId = cmd.Flags().String("project-id", ConfigInstance.ProjectID, "Optional. Assign project-id") + req.Region = cmd.Flags().String("region", ConfigInstance.Region, "Optional. Assign region") req.PayMode = cmd.Flags().String("charge-mode", "Bandwidth", "Optional. charge-mode is an enumeration value. 'Traffic','Bandwidth' or 'ShareBandwidth'") req.ShareBandwidthId = cmd.Flags().String("share-bandwidth-id", "", "Optional. ShareBandwidthId, required only when charge-mode is 'ShareBandwidth'") req.Quantity = cmd.Flags().Int("quantity", 1, "Optional. The duration of the instance. N years/months.") req.ChargeType = cmd.Flags().String("charge-type", "Month", "Optional. Enumeration value.'Year',pay yearly;'Month',pay monthly;'Dynamic', pay hourly(requires permission),'Trial', free trial(need permission)") - req.Tag = cmd.Flags().String("group", "Default", "Group of your EIP.") - req.Name = cmd.Flags().String("name", "EIP", "Name of your EIP.") - req.Remark = cmd.Flags().String("remark", "", "Remark of your EIP.") - req.CouponId = cmd.Flags().String("coupon-id", "", "Coupon ID, The Coupon can deducte part of the payment") + req.Tag = cmd.Flags().String("group", "Default", "Optional. Group of your EIP.") + req.Name = cmd.Flags().String("name", "EIP", "Optional. Name of your EIP.") + req.Remark = cmd.Flags().String("remark", "", "Optional. Remark of your EIP.") + req.CouponId = cmd.Flags().String("coupon-id", "", "Optional. Coupon ID, The Coupon can deducte part of the payment") + count = cmd.Flags().Int("count", 1, "Optional. Count of EIP to allocate") + cmd.Flags().SetFlagValues("line", "BGP", "International") cmd.Flags().SetFlagValues("charge-mode", "Bandwidth", "Traffic", "ShareBandwidth") cmd.Flags().SetFlagValues("charge-type", "Month", "Year", "Dynamic", "Trial") cmd.MarkFlagRequired("line") - cmd.MarkFlagRequired("bandwidth") + cmd.MarkFlagRequired("bandwidth-mb") return cmd } @@ -158,11 +222,11 @@ func NewCmdEIPBind() *cobra.Command { }, } cmd.Flags().SortFlags = false - projectID = cmd.Flags().String("project-id", ConfigInstance.ProjectID, "Assign project-id") - region = cmd.Flags().String("region", ConfigInstance.Region, "Assign region") - eipID = cmd.Flags().String("eip-id", "", "EIPId to bind. Required") - resourceID = cmd.Flags().String("resource-id", "", "ResourceID , which is the UHostId of uhost. Required") - resourceType = cmd.Flags().String("resource-type", "uhost", "ResourceType, type of resource to bind with eip. 'uhost','vrouter','ulb','upm','hadoophost'.eg..") + eipID = cmd.Flags().String("eip-id", "", "Required. EIPId to bind") + resourceID = cmd.Flags().String("resource-id", "", "Required. ResourceID , which is the UHostId of uhost") + resourceType = cmd.Flags().String("resource-type", "uhost", "Requried. ResourceType, type of resource to bind with eip. 'uhost','vrouter','ulb','upm','hadoophost'.eg..") + projectID = cmd.Flags().String("project-id", ConfigInstance.ProjectID, "Optional. Assign project-id") + region = cmd.Flags().String("region", ConfigInstance.Region, "Optional. Assign region") cmd.MarkFlagRequired("eip-id") cmd.MarkFlagRequired("resource-id") cmd.Flags().SetFlagValues("resource-type", "uhost", "vrouter", "ulb", "upm", "hadoophost", "fortresshost", "udockhost", "udhost", "natgw", "udb", "vpngw", "ucdr", "dbaudit") @@ -204,10 +268,10 @@ func NewCmdEIPUnbind() *cobra.Command { }, } cmd.Flags().SortFlags = false - req.ProjectId = cmd.Flags().String("project-id", ConfigInstance.ProjectID, "Assign project-id") - req.Region = cmd.Flags().String("region", ConfigInstance.Region, "Assign region") - req.EIPId = cmd.Flags().String("eip-id", "", "EIPId to unbind. Required") - req.ResourceId = cmd.Flags().String("resource-id", "", "ResourceID , which is the UHostId of uhost. Required") + req.EIPId = cmd.Flags().String("eip-id", "", "Required. EIPId to unbind") + req.ResourceId = cmd.Flags().String("resource-id", "", "Required. ResourceID , which is the UHostId of uhost") + req.ProjectId = cmd.Flags().String("project-id", ConfigInstance.ProjectID, "Optional. Assign project-id") + req.Region = cmd.Flags().String("region", ConfigInstance.Region, "Optional. Assign region") cmd.MarkFlagRequired("eip-id") cmd.MarkFlagRequired("resource-id") @@ -222,7 +286,7 @@ func NewCmdEIPRelease() *cobra.Command { Use: "release", Short: "Release EIP", Long: "Release EIP", - Example: "ucloud eip release --eip-id eip-xx1 --eip-id eip-xx2", + Example: "ucloud eip release --eip-id eip-xx1,eip-xx2", Run: func(cmd *cobra.Command, args []string) { for _, id := range ids { req.EIPId = &id @@ -235,9 +299,10 @@ func NewCmdEIPRelease() *cobra.Command { } }, } - req.ProjectId = cmd.Flags().String("project-id", ConfigInstance.ProjectID, "Assign project-id") - req.Region = cmd.Flags().String("region", ConfigInstance.Region, "Assign region") - cmd.Flags().StringArrayVarP(&ids, "eip-id", "", make([]string, 0), "EIPId of the EIP you want to release. Required") + cmd.Flags().SortFlags = false + cmd.Flags().StringSliceVarP(&ids, "eip-id", "", make([]string, 0), "Required. EIPIds of the EIP you want to release") + req.ProjectId = cmd.Flags().String("project-id", ConfigInstance.ProjectID, "Optional. Assign project-id") + req.Region = cmd.Flags().String("region", ConfigInstance.Region, "Optional. Assign region") cmd.MarkFlagRequired("eip-id") cmd.MarkFlagRequired("bandwidth") return cmd diff --git a/cmd/root.go b/cmd/root.go index 5700b187a9..dd837925fe 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -81,21 +81,27 @@ func NewCmdRoot() *cobra.Command { } const helpTmpl = `Usage:{{if .Runnable}} + {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}} [command]{{end}}{{if gt (len .Aliases) 0}} Aliases: + {{.NameAndAliases}}{{end}}{{if .HasExample}} Examples: -{{.Example}}{{end}}{{if .HasAvailableSubCommands}} + + {{.Example}}{{end}}{{if .HasAvailableSubCommands}} Commands:{{range .Commands}}{{if .IsAvailableCommand}} - {{rpad .Name .NamePadding }} {{.Short}}{{end}} - {{end}}{{end}}{{if .HasAvailableLocalFlags}} + + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} + Flags: + {{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} Global Flags: + {{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} diff --git a/cmd/uhost.go b/cmd/uhost.go index 1c65e29fbe..d031297128 100644 --- a/cmd/uhost.go +++ b/cmd/uhost.go @@ -16,6 +16,7 @@ package cmd import ( "fmt" + "net" "strings" "github.com/spf13/cobra" @@ -50,14 +51,15 @@ func NewCmdUHost() *cobra.Command { //UHostRow UHost表格行 type UHostRow struct { - UHostName string - ResourceID string - Group string - ClassicNetwork string - Config string - Type string - CreationTime string - State string + UHostName string + ResourceID string + Group string + PrivateIP string + PublicIP string + Config string + Type string + State string + CreationTime string } //NewCmdUHostList [ucloud uhost list] @@ -83,13 +85,13 @@ func NewCmdUHostList() *cobra.Command { row.ResourceID = host.UHostId row.Group = host.Tag for _, ip := range host.IPSet { - if row.ClassicNetwork != "" { - row.ClassicNetwork += " | " + if row.PublicIP != "" { + row.PublicIP += " | " } if ip.Type == "Private" { - row.ClassicNetwork += fmt.Sprintf("%s", ip.IP) + row.PrivateIP = ip.IP } else { - row.ClassicNetwork += fmt.Sprintf("%s %s", ip.IP, ip.Type) + row.PublicIP += fmt.Sprintf("%s %s", ip.IP, ip.Type) } } osName := strings.SplitN(host.OsName, " ", 2) @@ -107,7 +109,7 @@ func NewCmdUHostList() *cobra.Command { row.Type = host.UHostType + "/" + host.HostType list = append(list, row) } - base.PrintTable(list, []string{"UHostName", "ResourceID", "Group", "ClassicNetwork", "Config", "Type", "CreationTime", "State"}) + base.PrintTableS(list) } }, } @@ -118,7 +120,7 @@ func NewCmdUHostList() *cobra.Command { cmd.Flags().StringSliceVar(&req.UHostIds, "resource-id", make([]string, 0), "Optional. UHost Instance ID, multiple values separated by comma(without space)") req.Tag = cmd.Flags().String("group", "", "Optional. Group") req.Offset = cmd.Flags().Int("offset", 0, "Optional. Offset default 0") - req.Limit = cmd.Flags().Int("limit", 20, "Optional. Limit default 20, max value 100") + req.Limit = cmd.Flags().Int("limit", 50, "Optional. Limit default 50, max value 100") return cmd } @@ -126,10 +128,10 @@ func NewCmdUHostList() *cobra.Command { //NewCmdUHostCreate [ucloud uhost create] func NewCmdUHostCreate() *cobra.Command { var bindEipID *string + var async *bool + req := base.BizClient.NewCreateUHostInstanceRequest() eipReq := base.BizClient.NewAllocateEIPRequest() - - async := sdk.Bool(false) cmd := &cobra.Command{ Use: "create", Short: "Create UHost instance", @@ -160,10 +162,17 @@ func NewCmdUHostCreate() *cobra.Command { base.Cxt.Printf("UHost:%v created\n", resp.UHostIds) } - if *bindEipID != "" { - if len(resp.UHostIds) == 1 { - bindEIP(sdk.String(resp.UHostIds[0]), sdk.String("uhost"), bindEipID, req.ProjectId, req.Region) + if *bindEipID != "" && len(resp.UHostIds) == 1 { + ip := net.ParseIP(*bindEipID) + if ip != nil { + eipID, err := getEIPIDbyIP(ip, *req.ProjectId, *req.Region) + if err != nil { + base.HandleError(err) + } else { + *bindEipID = eipID + } } + bindEIP(sdk.String(resp.UHostIds[0]), sdk.String("uhost"), bindEipID, req.ProjectId, req.Region) } if *eipReq.OperatorName != "" && *eipReq.Bandwidth != 0 { @@ -210,7 +219,7 @@ func NewCmdUHostCreate() *cobra.Command { flags := cmd.Flags() flags.SortFlags = false - async = flags.Bool("async", false, "Optional. Display information about the operation in progress, without waiting for the operation to complete.") + async = flags.Bool("async", false, "Optional. Do not wait for the long-running operation to finish.") req.CPU = flags.Int("cpu", 4, "Required. The count of CPU cores. Optional parameters: {1, 2, 4, 8, 12, 16, 24, 32}") req.Memory = flags.Int("memory-gb", 8, "Required. Memory size. Unit: GB. Range: [1, 128], multiple of 2") req.Password = flags.String("password", "", "Required. Password of the uhost user(root/ubuntu)") @@ -218,7 +227,7 @@ func NewCmdUHostCreate() *cobra.Command { req.VPCId = flags.String("vpc-id", "", "Optional. VPC ID. This field is required under VPC2.0. See 'ucloud vpc list'") req.SubnetId = flags.String("subnet-id", "", "Optional. Subnet ID. This field is required under VPC2.0. See 'ucloud subnet list'") req.Name = flags.String("name", "UHost", "Optional. UHost instance name") - bindEipID = flags.String("bind-eip-id", "", "Optional. Bind eip to uhost") + bindEipID = flags.String("bind-eip", "", "Optional. Bind eip to uhost. Value could be resource id or IP Address") eipReq.OperatorName = flags.String("create-eip-line", "", "Optional. Required if you want to create new EIP. Line of created eip to bind with the uhost") eipReq.Bandwidth = cmd.Flags().Int("create-eip-bandwidth-mb", 0, "Optional. Required if you want to create new EIP. Bandwidth(Unit:Mbps).The range of value related to network charge mode. By traffic [1, 200]; by bandwidth [1,800] (Unit: Mbps); it could be 0 if the eip belong to the shared bandwidth") eipReq.PayMode = cmd.Flags().String("create-eip-charge-mode", "Bandwidth", "Optional. 'Traffic','Bandwidth' or 'ShareBandwidth'") @@ -299,7 +308,9 @@ func NewCmdUHostCreate() *cobra.Command { //NewCmdUHostDelete ucloud uhost delete func NewCmdUHostDelete() *cobra.Command { - isDestory := sdk.Bool(false) + var uhostIDs *[]string + var isDestory = sdk.Bool(false) + var yes *bool req := base.BizClient.NewTerminateUHostInstanceRequest() cmd := &cobra.Command{ @@ -307,50 +318,62 @@ func NewCmdUHostDelete() *cobra.Command { Short: "Delete Uhost instance", Long: "Delete Uhost instance", Run: func(cmd *cobra.Command, args []string) { - sure, err := ux.Prompt("Are you sure you want to delete this host?") - if err != nil { - base.Cxt.Println(err) - return - } - if !sure { - return + if !*yes { + sure, err := ux.Prompt("Are you sure you want to delete this host?") + if err != nil { + base.Cxt.Println(err) + return + } + if !sure { + return + } } if *isDestory { req.Destroy = sdk.Int(1) } else { req.Destroy = sdk.Int(0) } - hostIns, err := describeUHostByID(*req.UHostId, *req.ProjectId, *req.Region, *req.Zone) - if err != nil { - base.HandleError(err) - } else if hostIns != nil { - ins := hostIns.(*uhost.UHostInstanceSet) - if ins.State == "Running" { - _req := base.BizClient.NewStopUHostInstanceRequest() - _req.ProjectId = req.ProjectId - _req.Region = req.Region - _req.Zone = req.Zone - _req.UHostId = req.UHostId - stopUhostIns(_req) + for _, id := range *uhostIDs { + id = base.PickResourceID(id) + req.UHostId = &id + hostIns, err := describeUHostByID(*req.UHostId, *req.ProjectId, *req.Region, *req.Zone) + if err != nil { + base.HandleError(err) + } else if hostIns != nil { + ins := hostIns.(*uhost.UHostInstanceSet) + if ins.State == "Running" { + _req := base.BizClient.NewStopUHostInstanceRequest() + _req.ProjectId = req.ProjectId + _req.Region = req.Region + _req.Zone = req.Zone + _req.UHostId = req.UHostId + stopUhostIns(_req, false) + } + } + resp, err := base.BizClient.TerminateUHostInstance(req) + if err != nil { + base.HandleError(err) + } else { + base.Cxt.Printf("UHost:[%v] deleted\n", resp.UHostId) } - } - resp, err := base.BizClient.TerminateUHostInstance(req) - if err != nil { - base.HandleError(err) - } else { - base.Cxt.Printf("UHost:[%v] deleted\n", resp.UHostId) } }, } - - req.ProjectId = cmd.Flags().String("project-id", base.ConfigInstance.ProjectID, "Assign project-id") - req.Region = cmd.Flags().String("region", base.ConfigInstance.Region, "Assign region") - req.Zone = cmd.Flags().String("zone", "", "availability zone") - req.UHostId = cmd.Flags().String("resource-id", "", "ResourceID of the uhost instance( or UHostId)") - isDestory = cmd.Flags().Bool("destory", false, "false,the uhost instance will be thrown to UHost recycle If you have permission; true,the uhost instance will be deleted directly") - req.EIPReleased = cmd.Flags().String("eip-released", "false", "false,Unbind EIP only; true, Unbind EIP and release it") + cmd.Flags().SortFlags = false + uhostIDs = cmd.Flags().StringSlice("resource-id", nil, "Requried. ResourceIDs(UhostIds) of the uhost instance") + req.ProjectId = cmd.Flags().String("project-id", base.ConfigInstance.ProjectID, "Optional. Assign project-id") + req.Region = cmd.Flags().String("region", base.ConfigInstance.Region, "Optional. Assign region") + req.Zone = cmd.Flags().String("zone", "", "Optional. availability zone") + isDestory = cmd.Flags().Bool("destory", false, "Optional. false,the uhost instance will be thrown to UHost recycle If you have permission; true,the uhost instance will be deleted directly") + req.ReleaseEIP = cmd.Flags().Bool("release-eip", false, "Optional. false,Unbind EIP only; true, Unbind EIP and release it") + req.ReleaseUDisk = cmd.Flags().Bool("delete-cloud-disk", false, "Optional.false,Detach cloud disk only; true, Detach cloud disk and delete it") + yes = cmd.Flags().BoolP("yes", "y", false, "Optional. Do not prompt for confirmation.") cmd.Flags().SetFlagValues("destory", "true", "false") - cmd.Flags().SetFlagValues("eip-released", "true", "false") + cmd.Flags().SetFlagValues("release-eip", "true", "false") + cmd.Flags().SetFlagValues("delete-cloud-disk", "true", "false") + cmd.Flags().SetFlagValuesFunc("resource-id", func() []string { + return getUhostList([]string{status.HOST_RUNNING, status.HOST_FAIL, status.HOST_FAIL}, *req.ProjectId, *req.Region, *req.Zone) + }) cmd.MarkFlagRequired("resource-id") return cmd @@ -358,111 +381,172 @@ func NewCmdUHostDelete() *cobra.Command { //NewCmdUHostStop ucloud uhost stop func NewCmdUHostStop() *cobra.Command { + var uhostIDs *[]string + var async *bool req := base.BizClient.NewStopUHostInstanceRequest() cmd := &cobra.Command{ - Use: "stop", - Short: "Shut down uhost instance", - Long: "Shut down uhost instance", + Use: "stop", + Short: "Shut down uhost instance", + Long: "Shut down uhost instance", + Example: "ucloud uhost stop --resource-id uhost-xxx1,uhost-xxx2", Run: func(cmd *cobra.Command, args []string) { - stopUhostIns(req) + for _, id := range *uhostIDs { + id = base.PickResourceID(id) + req.UHostId = &id + stopUhostIns(req, *async) + } }, } - req.ProjectId = cmd.Flags().String("project-id", base.ConfigInstance.ProjectID, "Assign project-id") - req.Region = cmd.Flags().String("region", base.ConfigInstance.Region, "Assign region") - req.Zone = cmd.Flags().String("zone", "", "Assign availability zone") - req.UHostId = cmd.Flags().String("resource-id", "", "ResourceID of the uhost instance( or UHostId)") + cmd.Flags().SortFlags = false + uhostIDs = cmd.Flags().StringSlice("resource-id", nil, "Required. ResourceIDs(UHostIds) of the uhost instances") + req.ProjectId = cmd.Flags().String("project-id", base.ConfigInstance.ProjectID, "Optional. Assign project-id") + req.Region = cmd.Flags().String("region", base.ConfigInstance.Region, "Optional. Assign region") + req.Zone = cmd.Flags().String("zone", "", "Optional. Assign availability zone") + async = cmd.Flags().Bool("async", false, "Optional. Do not wait for the long-running operation to finish.") + cmd.Flags().SetFlagValuesFunc("resource-id", func() []string { + return getUhostList([]string{status.HOST_RUNNING}, *req.ProjectId, *req.Region, *req.Zone) + }) cmd.MarkFlagRequired("resource-id") return cmd } -func stopUhostIns(req *uhost.StopUHostInstanceRequest) { +func stopUhostIns(req *uhost.StopUHostInstanceRequest, async bool) { resp, err := base.BizClient.StopUHostInstance(req) if err != nil { base.HandleError(err) } else { - text := fmt.Sprintf("UHost:[%v] is shuting down", resp.UhostId) - done := pollUhost(resp.UhostId, *req.ProjectId, *req.Region, *req.Zone, []string{status.HOST_STOPPED, status.HOST_FAIL}) - ux.DotSpinner.Start(text) - <-done - ux.DotSpinner.Stop() + text := fmt.Sprintf("UHost:[%v] is shutting down", resp.UhostId) + if async { + base.Cxt.Println(text) + } else { + done := pollUhost(resp.UhostId, *req.ProjectId, *req.Region, *req.Zone, []string{status.HOST_STOPPED, status.HOST_FAIL}) + ux.DotSpinner.Start(text) + <-done + ux.DotSpinner.Stop() + } } } //NewCmdUHostStart ucloud uhost start func NewCmdUHostStart() *cobra.Command { + var async *bool + var uhostIDs *[]string req := base.BizClient.NewStartUHostInstanceRequest() cmd := &cobra.Command{ Use: "start", Short: "Start Uhost instance", Long: "Start Uhost instance", - Example: "ucloud uhost start --resource-id uhost-xxx", + Example: "ucloud uhost start --resource-id uhost-xxx1,uhost-xxx2", Run: func(cmd *cobra.Command, args []string) { - resp, err := base.BizClient.StartUHostInstance(req) - if err != nil { - base.HandleError(err) - } else { - text := fmt.Sprintf("UHost:[%v] is starting", resp.UhostId) - done := pollUhost(resp.UhostId, *req.ProjectId, *req.Region, *req.Zone, []string{status.HOST_RUNNING, status.HOST_FAIL}) - ux.DotSpinner.Start(text) - <-done - ux.DotSpinner.Stop() + for _, id := range *uhostIDs { + id := base.PickResourceID(id) + req.UHostId = &id + resp, err := base.BizClient.StartUHostInstance(req) + if err != nil { + base.HandleError(err) + } else { + text := fmt.Sprintf("UHost:[%v] is starting", resp.UhostId) + if *async { + base.Cxt.Println(text) + } else { + done := pollUhost(resp.UhostId, *req.ProjectId, *req.Region, *req.Zone, []string{status.HOST_RUNNING, status.HOST_FAIL}) + dotSpinner := ux.NewDotSpinner() + dotSpinner.Start(text) + <-done + dotSpinner.Stop() + } + } } }, } - req.ProjectId = cmd.Flags().String("project-id", base.ConfigInstance.ProjectID, "Assign project-id") - req.Region = cmd.Flags().String("region", base.ConfigInstance.Region, "Assign region") - req.Zone = cmd.Flags().String("zone", "", "Assign availability zone") - req.UHostId = cmd.Flags().String("resource-id", "", "ResourceID of the uhost instance( or UHostId)") - req.DiskPassword = cmd.Flags().String("disk-password", "", "Encrypted disk password") + cmd.Flags().SortFlags = false + uhostIDs = cmd.Flags().StringSlice("resource-id", nil, "Requried. ResourceIDs(UHostIds) of the uhost instance") + req.ProjectId = cmd.Flags().String("project-id", base.ConfigInstance.ProjectID, "Optional. Assign project-id") + req.Region = cmd.Flags().String("region", base.ConfigInstance.Region, "Optional. Assign region") + req.Zone = cmd.Flags().String("zone", "", "Optional. Assign availability zone") + req.DiskPassword = cmd.Flags().String("disk-password", "", "Optional. Encrypted disk password") + async = cmd.Flags().Bool("async", false, "Optional. Do not wait for the long-running operation to finish.") + cmd.Flags().SetFlagValuesFunc("resource-id", func() []string { + return getUhostList([]string{status.HOST_STOPPED}, *req.ProjectId, *req.Region, *req.Zone) + }) cmd.MarkFlagRequired("resource-id") return cmd } //NewCmdUHostReboot ucloud uhost restart func NewCmdUHostReboot() *cobra.Command { + var uhostIDs *[]string + var async *bool req := base.BizClient.NewRebootUHostInstanceRequest() cmd := &cobra.Command{ Use: "restart", - Short: "Restart/reboot Uhost instance", - Long: "Restart/reboot Uhost instance", - Example: "ucloud uhost restart --resource-id uhost-xxx", + Short: "Restart uhost instance", + Long: "Restart uhost instance", + Example: "ucloud uhost restart --resource-id uhost-xxx1,uhost-xxx2", Run: func(cmd *cobra.Command, args []string) { - resp, err := base.BizClient.RebootUHostInstance(req) - if err != nil { - base.HandleError(err) - } else { - text := fmt.Sprintf("UHost:[%v] is restarting", resp.UhostId) - done := pollUhost(resp.UhostId, *req.ProjectId, *req.Region, *req.Zone, []string{status.HOST_RUNNING, status.HOST_FAIL}) - ux.DotSpinner.Start(text) - <-done - ux.DotSpinner.Stop() + for _, id := range *uhostIDs { + id = base.PickResourceID(id) + req.UHostId = &id + resp, err := base.BizClient.RebootUHostInstance(req) + if err != nil { + base.HandleError(err) + } else { + text := fmt.Sprintf("UHost:[%v] is restarting", resp.UhostId) + if *async { + base.Cxt.Println(text) + } else { + done := pollUhost(resp.UhostId, *req.ProjectId, *req.Region, *req.Zone, []string{status.HOST_RUNNING, status.HOST_FAIL}) + ux.DotSpinner.Start(text) + <-done + ux.DotSpinner.Stop() + } + } } }, } - req.ProjectId = cmd.Flags().String("project-id", base.ConfigInstance.ProjectID, "Assign project-id") - req.Region = cmd.Flags().String("region", base.ConfigInstance.Region, "Assign region") - req.Zone = cmd.Flags().String("zone", "", "Assign availability zone") - req.UHostId = cmd.Flags().String("resource-id", "", "ResourceID of the uhost instance( or UHostId)") - req.DiskPassword = cmd.Flags().String("disk-password", "", "Encrypted disk password") + cmd.Flags().SortFlags = false + uhostIDs = cmd.Flags().StringSlice("resource-id", nil, "Required. ResourceIDs(UHostIds) of the uhost instance") + req.ProjectId = cmd.Flags().String("project-id", base.ConfigInstance.ProjectID, "Optional. Assign project-id") + req.Region = cmd.Flags().String("region", base.ConfigInstance.Region, "Optional. Assign region") + req.Zone = cmd.Flags().String("zone", "", "Optional. Assign availability zone") + req.DiskPassword = cmd.Flags().String("disk-password", "", "Optional. Encrypted disk password") + async = cmd.Flags().Bool("async", false, "Optional. Do not wait for the long-running operation to finish.") + cmd.Flags().SetFlagValuesFunc("resource-id", func() []string { + return getUhostList([]string{status.HOST_FAIL, status.HOST_RUNNING, status.HOST_STOPPED}, *req.ProjectId, *req.Region, *req.Zone) + }) cmd.MarkFlagRequired("resource-id") return cmd } //NewCmdUHostPoweroff ucloud uhost poweroff func NewCmdUHostPoweroff() *cobra.Command { + var yes *bool + var uhostIDs *[]string req := base.BizClient.NewPoweroffUHostInstanceRequest() cmd := &cobra.Command{ - Use: "poweroff", - Short: "Analog power off Uhost instnace", - Long: "Analog power off Uhost instnace", + Use: "poweroff", + Short: "Analog power off Uhost instnace", + Long: "Analog power off Uhost instnace", + Example: "ucloud uhost poweroff --resource-id uhost-xxx1,uhost-xxx2", Run: func(cmd *cobra.Command, args []string) { - sure, err := ux.Prompt("Danger, it may affect data integrity. Are you sure you want to poweroff this host? (y/n):") - if err != nil { - base.Cxt.Println(err) - return + if !*yes { + confirmText := "Danger, it may affect data integrity. Are you sure you want to poweroff this uhost?" + if len(*uhostIDs) > 1 { + confirmText = "Danger, it may affect data integrity. Are you sure you want to poweroff those uhosts?" + } + sure, err := ux.Prompt(confirmText) + if err != nil { + base.Cxt.Println(err) + return + } + if !sure { + return + } } - if sure { + for _, id := range *uhostIDs { + id = base.PickResourceID(id) + req.UHostId = &id resp, err := base.BizClient.PoweroffUHostInstance(req) if err != nil { base.HandleError(err) @@ -472,21 +556,26 @@ func NewCmdUHostPoweroff() *cobra.Command { } }, } + cmd.Flags().SortFlags = false + uhostIDs = cmd.Flags().StringSlice("resource-id", nil, "ResourceIDs(UHostIds) of the uhost instance") req.ProjectId = cmd.Flags().String("project-id", base.ConfigInstance.ProjectID, "Assign project-id") req.Region = cmd.Flags().String("region", base.ConfigInstance.Region, "Assign region") req.Zone = cmd.Flags().String("zone", "", "Assign availability zone") - req.UHostId = cmd.Flags().String("resource-id", "", "ResourceID of the uhost instance( or UHostId)") + yes = cmd.Flags().BoolP("yes", "y", false, "Optional. Do not prompt for confirmation.") cmd.MarkFlagRequired("resource-id") return cmd } //NewCmdUHostResize ucloud uhost resize func NewCmdUHostResize() *cobra.Command { + var yes *bool + var uhostIDs *[]string req := base.BizClient.NewResizeUHostInstanceRequest() cmd := &cobra.Command{ - Use: "resize", - Short: "Resize uhost instance,such as cpu core count, memory size and disk size", - Long: "Resize uhost instance,such as cpu core count, memory size and disk size", + Use: "resize", + Short: "Resize uhost instance,such as cpu core count, memory size and disk size", + Long: "Resize uhost instance,such as cpu core count, memory size and disk size", + Example: "ucloud uhost resize --resource-id uhost-xxx1,uhost-xxx2 --cpu 4 --memory-gb 8", Run: func(cmd *cobra.Command, args []string) { if *req.CPU == 0 { req.CPU = nil @@ -502,45 +591,61 @@ func NewCmdUHostResize() *cobra.Command { if *req.BootDiskSpace == 0 { req.BootDiskSpace = nil } - host, err := describeUHostByID(*req.UHostId, *req.ProjectId, *req.Region, *req.Zone) - if err != nil { - base.Cxt.Println(err) - return - } - inst := host.(*uhost.UHostInstanceSet) - if inst.State == "Running" { - agreeClose, err := ux.Prompt("Resize uhost must be after stop it. Do you want to stop this host? (y/n):") + for _, id := range *uhostIDs { + id = base.PickResourceID(id) + req.UHostId = &id + host, err := describeUHostByID(id, *req.ProjectId, *req.Region, *req.Zone) if err != nil { base.Cxt.Println(err) return } - if agreeClose { + inst := host.(*uhost.UHostInstanceSet) + if inst.State == "Running" { + if !*yes { + confirmText := "Resize uhost must be after stop it. Do you want to stop this uhost?" + if len(*uhostIDs) > 1 { + confirmText = "Resize uhost must be after stop it. Do you want to stop those uhosts?" + } + agreeClose, err := ux.Prompt(confirmText) + if err != nil { + base.Cxt.Println(err) + return + } + if !agreeClose { + continue + } + } _req := base.BizClient.NewStopUHostInstanceRequest() _req.ProjectId = req.ProjectId _req.Region = req.Region _req.Zone = req.Zone - _req.UHostId = req.UHostId - stopUhostIns(_req) + _req.UHostId = &id + stopUhostIns(_req, false) } - } - resp, err := base.BizClient.ResizeUHostInstance(req) - if err != nil { - base.HandleError(err) - } else { - base.Cxt.Printf("UHost:[%v] resized\n", resp.UhostId) + resp, err := base.BizClient.ResizeUHostInstance(req) + if err != nil { + base.HandleError(err) + } else { + base.Cxt.Printf("UHost:[%v] resized\n", resp.UhostId) + } } }, } - req.ProjectId = cmd.Flags().String("project-id", base.ConfigInstance.ProjectID, "Assign project-id") - req.Region = cmd.Flags().String("region", base.ConfigInstance.Region, "Assign region") - req.Zone = cmd.Flags().String("zone", "", "Assign availability zone") - req.UHostId = cmd.Flags().String("resource-id", "", "ResourceID of the uhost instance( or UHostId)") - req.CPU = cmd.Flags().Int("cpu", 0, "The number of virtual CPU cores. Series1 {1, 2, 4, 8, 12, 16, 24, 32}. Series2 {1,2,4,8,16}") - req.Memory = cmd.Flags().Int("memory", 0, "memory size. Unit: GB. Range: [1, 128], multiple of 2") - req.DiskSpace = cmd.Flags().Int("data-disk-size-gb", 0, "Data disk size,unit GB. Range[10,1000], SSD disk range[100,500]. Step 10") - req.BootDiskSpace = cmd.Flags().Int("system-disk-size-gb", 0, "System disk size, unit GB. Range[20,100]. Step 10. System disk does not support shrinkage") - req.NetCapValue = cmd.Flags().Int("net-cap", 0, "NIC scale. 1,upgrade; 2,downgrade; 0,unchanged") + cmd.Flags().SortFlags = false + uhostIDs = cmd.Flags().StringSlice("resource-id", nil, "Required. ResourceIDs(or UhostIDs) of the uhost instances") + req.ProjectId = cmd.Flags().String("project-id", base.ConfigInstance.ProjectID, "Optional. Assign project-id") + req.Region = cmd.Flags().String("region", base.ConfigInstance.Region, "Optional. Assign region") + req.Zone = cmd.Flags().String("zone", "", "Optional. Assign availability zone") + req.CPU = cmd.Flags().Int("cpu", 0, "Optional. The number of virtual CPU cores. Series1 {1, 2, 4, 8, 12, 16, 24, 32}. Series2 {1,2,4,8,16}") + req.Memory = cmd.Flags().Int("memory-gb", 0, "Optional. memory size. Unit: GB. Range: [1, 128], multiple of 2") + req.DiskSpace = cmd.Flags().Int("data-disk-size-gb", 0, "Optional. Data disk size,unit GB. Range[10,1000], SSD disk range[100,500]. Step 10") + req.BootDiskSpace = cmd.Flags().Int("system-disk-size-gb", 0, "Optional. System disk size, unit GB. Range[20,100]. Step 10. System disk does not support shrinkage") + req.NetCapValue = cmd.Flags().Int("net-cap", 0, "Optional. NIC scale. 1,upgrade; 2,downgrade; 0,unchanged") + yes = cmd.Flags().BoolP("yes", "y", false, "Optional. Do not prompt for confirmation.") + cmd.Flags().SetFlagValuesFunc("resource-id", func() []string { + return getUhostList([]string{status.HOST_RUNNING, status.HOST_STOPPED, status.HOST_FAIL}, *req.ProjectId, *req.Region, *req.Zone) + }) cmd.MarkFlagRequired("resource-id") return cmd } @@ -564,3 +669,25 @@ func describeUHostByID(uhostID, projectID, region, zone string) (interface{}, er return &resp.UHostSet[0], nil } + +func getUhostList(states []string, project, region, zone string) []string { + req := base.BizClient.NewDescribeUHostInstanceRequest() + req.ProjectId = sdk.String(project) + req.Region = sdk.String(region) + req.Zone = sdk.String(zone) + req.Limit = sdk.Int(50) + resp, err := base.BizClient.DescribeUHostInstance(req) + if err != nil { + //todo runtime log + return nil + } + list := []string{} + for _, host := range resp.UHostSet { + for _, s := range states { + if host.State == s { + list = append(list, host.UHostId+"/"+strings.Replace(host.Name, " ", "-", -1)) + } + } + } + return list +} diff --git a/ux/spinner.go b/ux/spinner.go index 1dac43f47a..0d268983af 100644 --- a/ux/spinner.go +++ b/ux/spinner.go @@ -32,8 +32,8 @@ func (s *Spinner) Start(doingText string) { func (s *Spinner) Stop() { s.ticker.Stop() s.reset() - s.output = fmt.Sprintf("%s...%s\n", s.DoingText, s.DoneText) - fmt.Printf(s.output) + output := fmt.Sprintf("%s...%s\n", s.DoingText, s.DoneText) + fmt.Printf(output) } func (s *Spinner) reset() { @@ -70,3 +70,8 @@ var spinnerFrames = []rune{'⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷ // DotSpinner dot spinner var DotSpinner = &Spinner{frames: spinnerFrames, framesPerSecond: 12, DoingText: "running", DoneText: "done"} + +//NewDotSpinner get new DotSpinner instance +func NewDotSpinner() *Spinner { + return &Spinner{frames: spinnerFrames, framesPerSecond: 12, DoingText: "running", DoneText: "done"} +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/terminate_uhost_instance.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/terminate_uhost_instance.go index 34194faeda..baafa4248a 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/terminate_uhost_instance.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/terminate_uhost_instance.go @@ -22,7 +22,9 @@ type TerminateUHostInstanceRequest struct { Destroy *int `required:"false"` // 是否释放绑定的EIP。yes: 解绑EIP后,并释放;其他值或不填:解绑EIP。 - EIPReleased *string `required:"false"` + // EIPReleased *string `required:"false"` + ReleaseEIP *bool `required:"false"` + ReleaseUDisk *bool `required:"false"` } // TerminateUHostInstanceResponse is response schema for TerminateUHostInstance action From 27e03599f7d4ff598d2fdee7a0f73d038be31458 Mon Sep 17 00:00:00 2001 From: lixiaojun Date: Mon, 5 Nov 2018 16:31:38 +0800 Subject: [PATCH 4/4] 0.1.5 --- Makefile | 2 +- base/config.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 2a3d80bf27..663ceb6e6d 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -export VERSION=0.1.4 +export VERSION=0.1.5 .PHONY : build build: diff --git a/base/config.go b/base/config.go index 3c83b09538..62bfb0622b 100644 --- a/base/config.go +++ b/base/config.go @@ -16,7 +16,7 @@ import ( const configFile = "config.json" //Version 版本号 -const Version = "0.1.4" +const Version = "0.1.5" //ConfigPath 配置文件路径