diff --git a/pkg/multicloud/ecloud/client.go b/pkg/multicloud/ecloud/client.go index 6a926e9d70..e2672e2da7 100644 --- a/pkg/multicloud/ecloud/client.go +++ b/pkg/multicloud/ecloud/client.go @@ -215,6 +215,14 @@ func (ec *SEcloudClient) doList(ctx context.Context, r IRequest, result interfac } func (ec *SEcloudClient) request(ctx context.Context, r IRequest) (jsonutils.JSONObject, error) { + jrbody, err := ec.doRequest(ctx, r) + if err != nil { + return nil, err + } + return r.ForMateResponseBody(jrbody) +} + +func (ec *SEcloudClient) doRequest(ctx context.Context, r IRequest) (jsonutils.JSONObject, error) { // sign ec.completeSingParams(r) signature := ec.signer.Sign(ec.buildStringToSign(r), "BC_SIGNATURE&") @@ -263,30 +271,7 @@ func (ec *SEcloudClient) request(ctx context.Context, r IRequest) (jsonutils.JSO return nil, errors.Wrapf(err, "unable to parsing json: %s", rbody) } } - if !jrbody.Contains("state") { - return nil, ErrMissKey{ - Key: "state", - Jo: jrbody, - } - } - state, _ := jrbody.GetString("state") - switch state { - case "OK": - if !jrbody.Contains("body") { - return nil, ErrMissKey{ - Key: "body", - Jo: jrbody, - } - } - body, _ := jrbody.Get("body") - return body, nil - default: - if jrbody.Contains("errorMessage") { - msg, _ := jrbody.GetString("errorMessage") - return nil, &httputils.JSONClientError{Code: 400, Details: msg} - } - return nil, &httputils.JSONClientError{Code: 400, Details: jrbody.String()} - } + return jrbody, nil } type ErrMissKey struct { diff --git a/pkg/multicloud/ecloud/monitor.go b/pkg/multicloud/ecloud/monitor.go new file mode 100644 index 0000000000..763fd703e8 --- /dev/null +++ b/pkg/multicloud/ecloud/monitor.go @@ -0,0 +1,135 @@ +package ecloud + +import ( + "context" + "time" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/util/timeutils" + "yunion.io/x/pkg/utils" + + "yunion.io/x/onecloud/pkg/httperrors" +) + +const ( + MONITOR_FETCH_REQUEST_ACTION = "v1.dawn.monitor.fetch" + MONITOR_PRODUCT_REQUEST_ACTION = "v1.dawn.monitor.products" + + MONITOR_FETCH_SERVER_PATH = "/api/edw/edw/api" + + REQUEST_SUCCESS_CODE = "000000" +) + +var ( + noMetricRegion = []string{"guangzhou-2", "beijing-1", "hunan-1"} + + portRegionMap = map[string][]string{ + "8443": []string{"wuxi-1", "dongguan-1", "yaan-1", "zhengzhou-1", "beijing-2", "zhuzhou-1", "jinan-1", + "xian-1", "shanghai-1", "chongqing-1", "ningbo-1"}, + "18080": []string{"tianjin-1", "jilin-1", "hubei-1", "jiangxi-1", "gansu-1", "shanxi-1", "liaoning-1", + "yunnan-2", "hebei-1", "fujian-1", "guangxi-1", "anhui-1", "huhehaote-1", "guiyang-1"}, + } +) + +type Metric struct { + Name string `json:"MetricName"` +} + +type MetricData struct { + Entitys []Entity `json:"entity"` +} + +type Entity struct { + ResourceID string `json:"resourceId"` + MetricName string `json:"metricName"` + MetricNameCN string `json:"metricNameCn"` + Unit string `json:"unit"` + MaxValue int64 `json:"maxValue"` + AvgValue int64 `json:"avgValue"` + MinValue int64 `json:"minValue"` + Granularity string `json:"granularity"` + PolymerizeType string `json:"polymerizeType"` + SelectedMetricItem interface{} `json:"selectedMetricItem"` + MetricItems interface{} `json:"metricItems"` + IsChildnode bool `json:"isChildnode"` + Datapoints []Datapoint `json:"datapoints"` +} + +type Datapoint []string + +type SMonitorRequest struct { + SApiRequest +} + +func NewMonitorRequest(regionId string, serverPath string, query map[string]string, data jsonutils.JSONObject) *SMonitorRequest { + apiRequest := NewApiRequest(regionId, serverPath, query, data) + return &SMonitorRequest{*apiRequest} +} + +func (rr *SMonitorRequest) GetPort() string { + for port, regions := range portRegionMap { + if utils.IsInStringArray(rr.GetRegionId(), regions) { + return port + } + } + return "8443" +} + +func (br *SMonitorRequest) ForMateResponseBody(jrbody jsonutils.JSONObject) (jsonutils.JSONObject, error) { + code, _ := jrbody.GetString("code") + if code != REQUEST_SUCCESS_CODE { + message, _ := jrbody.(*jsonutils.JSONDict).GetString("message") + return nil, httperrors.NewBadRequestError("rep body code is :%s, message:%s,body:%v", code, message, jrbody) + } + if jrbody == nil || !jrbody.Contains("entity") { + return nil, ErrMissKey{ + Key: "entity", + Jo: jrbody, + } + } + return jrbody, nil +} + +func (r *SRegion) DescribeMetricList(productType string, metrics []Metric, resourceId string, + since time.Time, until time.Time) (MetricData, error) { + metricData := MetricData{ + Entitys: make([]Entity, 0), + } + if utils.IsInStringArray(r.GetId(), noMetricRegion) { + return metricData, httperrors.NewInputParameterError("region:%s no support pull metric at the moment", r.GetName()) + } + if metrics == nil || len(metrics) == 0 { + return metricData, httperrors.NewInputParameterError("metrics is empty") + } + params := map[string]string{ + "eAction": MONITOR_FETCH_REQUEST_ACTION, + } + getBody := jsonutils.NewDict() + getBody.Set("startTime", jsonutils.NewString(since.Format(timeutils.MysqlTimeFormat))) + getBody.Set("endTime", jsonutils.NewString(until.Format(timeutils.MysqlTimeFormat))) + getBody.Set("productType", jsonutils.NewString(productType)) + getBody.Set("resourceId", jsonutils.NewString(resourceId)) + getBody.Set("metrics", jsonutils.Marshal(&metrics)) + request := NewMonitorRequest(r.ID, MONITOR_FETCH_SERVER_PATH, params, getBody) + + err := r.client.doGet(context.Background(), request, &metricData) + if err != nil { + return metricData, errors.Wrap(err, "client doGet error") + } + + return metricData, nil +} + +func (r *SRegion) GetProductTypes() (jsonutils.JSONObject, error) { + params := map[string]string{ + "eAction": MONITOR_PRODUCT_REQUEST_ACTION, + } + request := NewMonitorRequest(r.ID, MONITOR_FETCH_SERVER_PATH, params, nil) + rtn := jsonutils.NewDict() + err := r.client.doGet(context.Background(), request, rtn) + if err != nil { + return nil, errors.Wrap(err, "client doGet error") + } + return rtn, nil +} diff --git a/pkg/multicloud/ecloud/request.go b/pkg/multicloud/ecloud/request.go index 7195fb1206..aa2131d4dc 100644 --- a/pkg/multicloud/ecloud/request.go +++ b/pkg/multicloud/ecloud/request.go @@ -22,6 +22,8 @@ import ( "time" "yunion.io/x/jsonutils" + + "yunion.io/x/onecloud/pkg/util/httputils" ) type IRequest interface { @@ -42,6 +44,7 @@ type IRequest interface { GetHTTPSInsecure() bool SetHTTPSInsecure(bool) GetUserAgent() map[string]string + ForMateResponseBody(jrbody jsonutils.JSONObject) (jsonutils.JSONObject, error) } type SJSONRequest struct { @@ -225,3 +228,30 @@ func (br *SBaseRequest) SetHTTPSInsecure(isInsecure bool) { func (br *SBaseRequest) GetUserAgent() map[string]string { return nil } + +func (br *SBaseRequest) ForMateResponseBody(jrbody jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if jrbody == nil || !jrbody.Contains("state") { + return nil, ErrMissKey{ + Key: "state", + Jo: jrbody, + } + } + state, _ := jrbody.GetString("state") + switch state { + case "OK": + if !jrbody.Contains("body") { + return nil, ErrMissKey{ + Key: "body", + Jo: jrbody, + } + } + body, _ := jrbody.Get("body") + return body, nil + default: + if jrbody.Contains("errorMessage") { + msg, _ := jrbody.GetString("errorMessage") + return nil, &httputils.JSONClientError{Code: 400, Details: msg} + } + return nil, &httputils.JSONClientError{Code: 400, Details: jrbody.String()} + } +} diff --git a/pkg/multicloud/ecloud/shell/monitor.go b/pkg/multicloud/ecloud/shell/monitor.go new file mode 100644 index 0000000000..4ec45d3dfc --- /dev/null +++ b/pkg/multicloud/ecloud/shell/monitor.go @@ -0,0 +1,20 @@ +package shell + +import ( + "yunion.io/x/onecloud/pkg/multicloud/ecloud" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type PListOptions struct { + } + shellutils.R(&PListOptions{}, "server-producttype-list", "List productTypes", func(cli *ecloud.SRegion, + args *PListOptions) error { + prod, e := cli.GetProductTypes() + if e != nil { + return e + } + printObject(prod) + return nil + }) +}