yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/hcs/monitor.go (about)

     1  // Copyright 2019 Yunion
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package hcs
    16  
    17  import (
    18  	"fmt"
    19  	"time"
    20  
    21  	"yunion.io/x/jsonutils"
    22  	"yunion.io/x/log"
    23  	"yunion.io/x/pkg/errors"
    24  
    25  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    26  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    27  	"yunion.io/x/onecloud/pkg/util/httputils"
    28  )
    29  
    30  type MetricData struct {
    31  	Namespace  string
    32  	MetricName string
    33  	Dimensions []struct {
    34  		Name  string
    35  		Value string
    36  	}
    37  	Datapoints []struct {
    38  		Average   float64
    39  		Timestamp int64
    40  	}
    41  	Unit string
    42  }
    43  
    44  func (self *SHcsClient) monitorPost(resource string, params map[string]interface{}) (jsonutils.JSONObject, error) {
    45  	url := self._url("ces", "V1.0", self.defaultRegion, resource)
    46  	return self.request(httputils.POST, url, nil, params)
    47  }
    48  
    49  func (self *SHcsClient) getServerMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
    50  	params := map[string]interface{}{
    51  		"from":   fmt.Sprintf("%d", opts.StartTime.UnixMilli()),
    52  		"to":     fmt.Sprintf("%d", opts.EndTime.UnixMilli()),
    53  		"period": "1",
    54  		"filter": "average",
    55  	}
    56  	metrics := []interface{}{}
    57  	namespace, dimesionName, metricNames := "SYS.ECS", "instance_id", []string{
    58  		"cpu_util",
    59  		"network_incoming_bytes_aggregate_rate",
    60  		"network_outgoing_bytes_aggregate_rate",
    61  		"disk_read_bytes_rate",
    62  		"disk_write_bytes_rate",
    63  		"disk_read_requests_rate",
    64  		"disk_write_requests_rate",
    65  	}
    66  	for _, metricName := range metricNames {
    67  		metrics = append(metrics, map[string]interface{}{
    68  			"namespace":   namespace,
    69  			"metric_name": metricName,
    70  			"dimensions": []map[string]string{
    71  				{
    72  					"name":  dimesionName,
    73  					"value": opts.ResourceId,
    74  				},
    75  			},
    76  		})
    77  	}
    78  	params["metrics"] = metrics
    79  	resp, err := self.monitorPost("batch-query-metric-data", params)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  	metricData := []MetricData{}
    84  	err = resp.Unmarshal(&metricData, "metrics")
    85  	if err != nil {
    86  		return nil, errors.Wrapf(err, "resp.Unmarshal")
    87  	}
    88  	result := []cloudprovider.MetricValues{}
    89  	for i := range metricData {
    90  		ret := cloudprovider.MetricValues{
    91  			Id:     opts.ResourceId,
    92  			Unit:   metricData[i].Unit,
    93  			Values: []cloudprovider.MetricValue{},
    94  		}
    95  		tags := map[string]string{}
    96  		switch metricData[i].MetricName {
    97  		case "cpu_util":
    98  			ret.MetricType = cloudprovider.VM_METRIC_TYPE_CPU_USAGE
    99  		case "network_incoming_bytes_aggregate_rate":
   100  			ret.MetricType = cloudprovider.VM_METRIC_TYPE_NET_BPS_RX
   101  			tags = map[string]string{"net_type": "internet"}
   102  		case "network_outgoing_bytes_aggregate_rate":
   103  			ret.MetricType = cloudprovider.VM_METRIC_TYPE_NET_BPS_TX
   104  			tags = map[string]string{"net_type": "internet"}
   105  		case "disk_read_bytes_rate":
   106  			ret.MetricType = cloudprovider.VM_METRIC_TYPE_DISK_IO_READ_BPS
   107  		case "disk_write_bytes_rate":
   108  			ret.MetricType = cloudprovider.VM_METRIC_TYPE_DISK_IO_WRITE_BPS
   109  		case "disk_read_requests_rate":
   110  			ret.MetricType = cloudprovider.VM_METRIC_TYPE_DISK_IO_READ_IOPS
   111  		case "disk_write_requests_rate":
   112  			ret.MetricType = cloudprovider.VM_METRIC_TYPE_DISK_IO_WRITE_IOPS
   113  		default:
   114  			log.Warningf("invalid metricName %s for %s %s", metricData[i].MetricName, opts.ResourceType, opts.ResourceId)
   115  			continue
   116  		}
   117  		for _, value := range metricData[i].Datapoints {
   118  			metricValue := cloudprovider.MetricValue{
   119  				Value:     value.Average,
   120  				Timestamp: time.UnixMilli(value.Timestamp),
   121  				Tags:      tags,
   122  			}
   123  			ret.Values = append(ret.Values, metricValue)
   124  		}
   125  		result = append(result, ret)
   126  	}
   127  	return result, nil
   128  }
   129  
   130  func (self *SHcsClient) getRedisMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
   131  	params := map[string]interface{}{
   132  		"from":   fmt.Sprintf("%d", opts.StartTime.UnixMilli()),
   133  		"to":     fmt.Sprintf("%d", opts.EndTime.UnixMilli()),
   134  		"period": "1",
   135  		"filter": "average",
   136  	}
   137  	metrics := []interface{}{}
   138  	namespace, dimesionName, metricNames := "SYS.DCS", "dcs_instance_id", []string{
   139  		"cpu_usage",
   140  		"memory_usage",
   141  		"instantaneous_input_kbps",
   142  		"instantaneous_output_kbps",
   143  		"connected_clients",
   144  		"instantaneous_ops",
   145  		"keys",
   146  		"expires",
   147  		"used_memory_dataset",
   148  	}
   149  	for _, metricName := range metricNames {
   150  		metrics = append(metrics, map[string]interface{}{
   151  			"namespace":   namespace,
   152  			"metric_name": metricName,
   153  			"dimensions": []map[string]string{
   154  				{
   155  					"name":  dimesionName,
   156  					"value": opts.ResourceId,
   157  				},
   158  			},
   159  		})
   160  	}
   161  	params["metrics"] = metrics
   162  	resp, err := self.monitorPost("batch-query-metric-data", params)
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  	metricData := []MetricData{}
   167  	err = resp.Unmarshal(&metricData, "metrics")
   168  	if err != nil {
   169  		return nil, errors.Wrapf(err, "resp.Unmarshal")
   170  	}
   171  	result := []cloudprovider.MetricValues{}
   172  	for i := range metricData {
   173  		ret := cloudprovider.MetricValues{
   174  			Id:     opts.ResourceId,
   175  			Unit:   metricData[i].Unit,
   176  			Values: []cloudprovider.MetricValue{},
   177  		}
   178  		tags := map[string]string{}
   179  		switch metricData[i].MetricName {
   180  		case "cpu_usage":
   181  			ret.MetricType = cloudprovider.REDIS_METRIC_TYPE_CPU_USAGE
   182  		case "memory_usage":
   183  			ret.MetricType = cloudprovider.REDIS_METRIC_TYPE_MEM_USAGE
   184  		case "instantaneous_input_kbps":
   185  			ret.MetricType = cloudprovider.REDIS_METRIC_TYPE_NET_BPS_RX
   186  		case "instantaneous_output_kbps":
   187  			ret.MetricType = cloudprovider.REDIS_METRIC_TYPE_NET_BPS_TX
   188  		case "connected_clients":
   189  			ret.MetricType = cloudprovider.REDIS_METRIC_TYPE_USED_CONN
   190  		case "instantaneous_ops":
   191  			ret.MetricType = cloudprovider.REDIS_METRIC_TYPE_OPT_SES
   192  		case "keys":
   193  			ret.MetricType = cloudprovider.REDIS_METRIC_TYPE_CACHE_KEYS
   194  		case "expires":
   195  			ret.MetricType = cloudprovider.REDIS_METRIC_TYPE_CACHE_EXP_KEYS
   196  		case "used_memory_dataset":
   197  			ret.MetricType = cloudprovider.REDIS_METRIC_TYPE_DATA_MEM_USAGE
   198  		default:
   199  			log.Warningf("invalid metricName %s for %s %s", metricData[i].MetricName, opts.ResourceType, opts.ResourceId)
   200  			continue
   201  		}
   202  		for _, value := range metricData[i].Datapoints {
   203  			metricValue := cloudprovider.MetricValue{
   204  				Value:     value.Average,
   205  				Timestamp: time.UnixMilli(value.Timestamp),
   206  				Tags:      tags,
   207  			}
   208  			ret.Values = append(ret.Values, metricValue)
   209  		}
   210  		result = append(result, ret)
   211  	}
   212  	return result, nil
   213  }
   214  
   215  func (self *SHcsClient) getRdsMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
   216  	params := map[string]interface{}{
   217  		"from":   fmt.Sprintf("%d", opts.StartTime.UnixMilli()),
   218  		"to":     fmt.Sprintf("%d", opts.EndTime.UnixMilli()),
   219  		"period": "1",
   220  		"filter": "average",
   221  	}
   222  	metrics := []interface{}{}
   223  	namespace, dimesionName, metricNames := "SYS.RDS", "rds_cluster_id", []string{
   224  		"rds001_cpu_util",
   225  		"rds002_mem_util",
   226  		"rds004_bytes_in",
   227  		"rds004_bytes_in",
   228  		"rds005_bytes_out",
   229  		"rds039_disk_util",
   230  		"rds049_disk_read_throughput",
   231  		"rds050_disk_write_throughput",
   232  		"rds006_conn_count",
   233  		"rds008_qps",
   234  		"rds009_tps",
   235  		"rds013_innodb_reads",
   236  		"rds014_innodb_writes",
   237  	}
   238  	switch opts.Engine {
   239  	case api.DBINSTANCE_TYPE_POSTGRESQL:
   240  		dimesionName = "postgresql_cluster_id"
   241  	case api.DBINSTANCE_TYPE_SQLSERVER:
   242  		dimesionName = "rds_cluster_sqlserver_id"
   243  	}
   244  
   245  	for _, metricName := range metricNames {
   246  		metrics = append(metrics, map[string]interface{}{
   247  			"namespace":   namespace,
   248  			"metric_name": metricName,
   249  			"dimensions": []map[string]string{
   250  				{
   251  					"name":  dimesionName,
   252  					"value": opts.ResourceId,
   253  				},
   254  			},
   255  		})
   256  	}
   257  	params["metrics"] = metrics
   258  	resp, err := self.monitorPost("batch-query-metric-data", params)
   259  	if err != nil {
   260  		return nil, err
   261  	}
   262  	metricData := []MetricData{}
   263  	err = resp.Unmarshal(&metricData, "metrics")
   264  	if err != nil {
   265  		return nil, errors.Wrapf(err, "resp.Unmarshal")
   266  	}
   267  	result := []cloudprovider.MetricValues{}
   268  	for i := range metricData {
   269  		ret := cloudprovider.MetricValues{
   270  			Id:     opts.ResourceId,
   271  			Unit:   metricData[i].Unit,
   272  			Values: []cloudprovider.MetricValue{},
   273  		}
   274  		tags := map[string]string{}
   275  		switch metricData[i].MetricName {
   276  		case "rds001_cpu_util":
   277  			ret.MetricType = cloudprovider.RDS_METRIC_TYPE_CPU_USAGE
   278  		case "rds002_mem_util":
   279  			ret.MetricType = cloudprovider.RDS_METRIC_TYPE_MEM_USAGE
   280  		case "rds004_bytes_in":
   281  			ret.MetricType = cloudprovider.RDS_METRIC_TYPE_NET_BPS_RX
   282  		case "rds005_bytes_out":
   283  			ret.MetricType = cloudprovider.RDS_METRIC_TYPE_NET_BPS_TX
   284  		case "rds039_disk_util":
   285  			ret.MetricType = cloudprovider.RDS_METRIC_TYPE_DISK_USAGE
   286  		case "rds049_disk_read_throughput":
   287  			ret.MetricType = cloudprovider.RDS_METRIC_TYPE_DISK_READ_BPS
   288  		case "rds050_disk_write_throughput":
   289  			ret.MetricType = cloudprovider.RDS_METRIC_TYPE_DISK_WRITE_BPS
   290  		case "rds006_conn_count":
   291  			ret.MetricType = cloudprovider.RDS_METRIC_TYPE_CONN_COUNT
   292  		case "rds008_qps":
   293  			ret.MetricType = cloudprovider.RDS_METRIC_TYPE_QPS
   294  		case "rds009_tps":
   295  			ret.MetricType = cloudprovider.RDS_METRIC_TYPE_TPS
   296  		case "rds013_innodb_reads":
   297  			ret.MetricType = cloudprovider.RDS_METRIC_TYPE_INNODB_READ_BPS
   298  		case "rds014_innodb_writes":
   299  			ret.MetricType = cloudprovider.RDS_METRIC_TYPE_INNODB_WRITE_BPS
   300  		default:
   301  			log.Warningf("invalid metricName %s for %s %s", metricData[i].MetricName, opts.ResourceType, opts.ResourceId)
   302  			continue
   303  		}
   304  		for _, value := range metricData[i].Datapoints {
   305  			metricValue := cloudprovider.MetricValue{
   306  				Value:     value.Average,
   307  				Timestamp: time.UnixMilli(value.Timestamp),
   308  				Tags:      tags,
   309  			}
   310  			ret.Values = append(ret.Values, metricValue)
   311  		}
   312  		result = append(result, ret)
   313  	}
   314  	return result, nil
   315  }
   316  
   317  func (self *SHcsClient) getBucketMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
   318  	params := map[string]interface{}{
   319  		"from":   opts.StartTime.UnixMilli(),
   320  		"to":     opts.EndTime.UnixMilli(),
   321  		"period": "1",
   322  		"filter": "average",
   323  	}
   324  	metrics := []interface{}{}
   325  	namespace, dimesionName, metricNames := "SYS.OBS", "bucket_name", []string{
   326  		"download_bytes",
   327  		"upload_bytes",
   328  		"first_byte_latency",
   329  		"get_request_count",
   330  		"request_count_4xx",
   331  		"request_count_5xx",
   332  	}
   333  
   334  	for _, metricName := range metricNames {
   335  		metrics = append(metrics, map[string]interface{}{
   336  			"namespace":   namespace,
   337  			"metric_name": metricName,
   338  			"dimensions": []map[string]string{
   339  				{
   340  					"name":  dimesionName,
   341  					"value": opts.ResourceId,
   342  				},
   343  			},
   344  		})
   345  	}
   346  	params["metrics"] = metrics
   347  	resp, err := self.monitorPost("batch-query-metric-data", params)
   348  	if err != nil {
   349  		return nil, err
   350  	}
   351  	metricData := []MetricData{}
   352  	err = resp.Unmarshal(&metricData, "metrics")
   353  	if err != nil {
   354  		return nil, errors.Wrapf(err, "resp.Unmarshal")
   355  	}
   356  	result := []cloudprovider.MetricValues{}
   357  	for i := range metricData {
   358  		ret := cloudprovider.MetricValues{
   359  			Id:     opts.ResourceId,
   360  			Unit:   metricData[i].Unit,
   361  			Values: []cloudprovider.MetricValue{},
   362  		}
   363  		tags := map[string]string{}
   364  		switch metricData[i].MetricName {
   365  		case "download_bytes":
   366  			ret.MetricType = cloudprovider.BUCKET_METRIC_TYPE_NET_BPS_TX
   367  		case "upload_bytes":
   368  			ret.MetricType = cloudprovider.BUCKET_METRIC_TYPE_NET_BPS_RX
   369  		case "first_byte_latency":
   370  			ret.MetricType = cloudprovider.BUCKET_METRIC_TYPE_LATECY
   371  			tags = map[string]string{"request": "get"}
   372  		case "get_request_count":
   373  			ret.MetricType = cloudprovider.BUCKET_METRYC_TYPE_REQ_COUNT
   374  			tags = map[string]string{"request": "get"}
   375  		case "request_count_4xx":
   376  			ret.MetricType = cloudprovider.BUCKET_METRYC_TYPE_REQ_COUNT
   377  			tags = map[string]string{"request": "4xx"}
   378  		case "request_count_5xx":
   379  			ret.MetricType = cloudprovider.BUCKET_METRYC_TYPE_REQ_COUNT
   380  			tags = map[string]string{"request": "5xx"}
   381  		default:
   382  			log.Warningf("invalid metricName %s for %s %s", metricData[i].MetricName, opts.ResourceType, opts.ResourceId)
   383  			continue
   384  		}
   385  		for _, value := range metricData[i].Datapoints {
   386  			metricValue := cloudprovider.MetricValue{
   387  				Value:     value.Average,
   388  				Timestamp: time.UnixMilli(value.Timestamp),
   389  				Tags:      tags,
   390  			}
   391  			ret.Values = append(ret.Values, metricValue)
   392  		}
   393  		result = append(result, ret)
   394  	}
   395  	return result, nil
   396  }
   397  
   398  func (self *SHcsClient) getLoadbalancerMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
   399  	params := map[string]interface{}{
   400  		"from":   fmt.Sprintf("%d", opts.StartTime.UnixMilli()),
   401  		"to":     fmt.Sprintf("%d", opts.EndTime.UnixMilli()),
   402  		"period": "1",
   403  		"filter": "average",
   404  	}
   405  	metrics := []interface{}{}
   406  	namespace, dimesionName, metricNames := "SYS.ELB", "lb_instance_id", []string{
   407  		"m7_in_Bps",
   408  		"m8_out_Bps",
   409  		"mc_l7_http_2xx",
   410  		"md_l7_http_3xx",
   411  		"me_l7_http_4xx",
   412  		"mf_l7_http_5xx",
   413  	}
   414  
   415  	for _, metricName := range metricNames {
   416  		metrics = append(metrics, map[string]interface{}{
   417  			"namespace":   namespace,
   418  			"metric_name": metricName,
   419  			"dimensions": []map[string]string{
   420  				{
   421  					"name":  dimesionName,
   422  					"value": opts.ResourceId,
   423  				},
   424  			},
   425  		})
   426  	}
   427  	params["metrics"] = metrics
   428  	resp, err := self.monitorPost("batch-query-metric-data", params)
   429  	if err != nil {
   430  		return nil, err
   431  	}
   432  	metricData := []MetricData{}
   433  	err = resp.Unmarshal(&metricData, "metrics")
   434  	if err != nil {
   435  		return nil, errors.Wrapf(err, "resp.Unmarshal")
   436  	}
   437  	result := []cloudprovider.MetricValues{}
   438  	for i := range metricData {
   439  		ret := cloudprovider.MetricValues{
   440  			Id:     opts.ResourceId,
   441  			Unit:   metricData[i].Unit,
   442  			Values: []cloudprovider.MetricValue{},
   443  		}
   444  		tags := map[string]string{}
   445  		switch metricData[i].MetricName {
   446  		case "m7_in_Bps":
   447  			ret.MetricType = cloudprovider.LB_METRIC_TYPE_NET_BPS_RX
   448  		case "m8_out_Bps":
   449  			ret.MetricType = cloudprovider.LB_METRIC_TYPE_NET_BPS_TX
   450  		case "mc_l7_http_2xx":
   451  			ret.MetricType = cloudprovider.LB_METRIC_TYPE_HRSP_COUNT
   452  			tags = map[string]string{"request": "2xx"}
   453  		case "md_l7_http_3xx":
   454  			ret.MetricType = cloudprovider.LB_METRIC_TYPE_HRSP_COUNT
   455  			tags = map[string]string{"request": "3xx"}
   456  		case "md_l7_http_4xx":
   457  			ret.MetricType = cloudprovider.LB_METRIC_TYPE_HRSP_COUNT
   458  			tags = map[string]string{"request": "4xx"}
   459  		case "md_l7_http_5xx":
   460  			ret.MetricType = cloudprovider.LB_METRIC_TYPE_HRSP_COUNT
   461  			tags = map[string]string{"request": "5xx"}
   462  		default:
   463  			log.Warningf("invalid metricName %s for %s %s", metricData[i].MetricName, opts.ResourceType, opts.ResourceId)
   464  			continue
   465  		}
   466  		for _, value := range metricData[i].Datapoints {
   467  			metricValue := cloudprovider.MetricValue{
   468  				Value:     value.Average,
   469  				Timestamp: time.UnixMilli(value.Timestamp),
   470  				Tags:      tags,
   471  			}
   472  			ret.Values = append(ret.Values, metricValue)
   473  		}
   474  		result = append(result, ret)
   475  	}
   476  	return result, nil
   477  }
   478  
   479  func (self *SHcsClient) GetMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
   480  	switch opts.ResourceType {
   481  	case cloudprovider.METRIC_RESOURCE_TYPE_SERVER:
   482  		return self.getServerMetrics(opts)
   483  	case cloudprovider.METRIC_RESOURCE_TYPE_REDIS:
   484  		return self.getRedisMetrics(opts)
   485  	case cloudprovider.METRIC_RESOURCE_TYPE_RDS:
   486  		return self.getRdsMetrics(opts)
   487  	case cloudprovider.METRIC_RESOURCE_TYPE_BUCKET:
   488  		return self.getBucketMetrics(opts)
   489  	case cloudprovider.METRIC_RESOURCE_TYPE_LB:
   490  		return self.getLoadbalancerMetrics(opts)
   491  	default:
   492  		return nil, errors.Wrapf(cloudprovider.ErrNotSupported, "%s", opts.ResourceType)
   493  	}
   494  }