yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/qcloud/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 qcloud
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  	"time"
    21  
    22  	"yunion.io/x/jsonutils"
    23  	"yunion.io/x/log"
    24  	"yunion.io/x/pkg/errors"
    25  	"yunion.io/x/pkg/util/timeutils"
    26  
    27  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    28  )
    29  
    30  const (
    31  	QCLOUD_API_VERSION_METRICS = "2018-07-24"
    32  )
    33  
    34  type SQcMetricDimension struct {
    35  	Name  string `json:"Name"`
    36  	Value string `json:"Value"`
    37  }
    38  
    39  type SQcMetricConditionDimension struct {
    40  	Key      string `json:"Key"`
    41  	Value    string `json:"Value"`
    42  	Operator string `json:"Operator"`
    43  }
    44  
    45  type SQcInstanceMetricDimension struct {
    46  	Dimensions []SQcMetricDimension
    47  }
    48  
    49  type SDataPoint struct {
    50  	Dimensions []SQcMetricDimension `json:"Dimensions"`
    51  	Timestamps []float64            `json:"Timestamps"`
    52  	Values     []float64            `json:"Values"`
    53  }
    54  
    55  type SK8SDataPoint struct {
    56  	MetricName string      `json:"MetricName"`
    57  	Points     []SK8sPoint `json:"Points"`
    58  }
    59  
    60  type SK8sPoint struct {
    61  	Dimensions []SQcMetricDimension `json:"Dimensions"`
    62  	Values     []SK8sPointValue     `json:"Values"`
    63  }
    64  
    65  type SK8sPointValue struct {
    66  	Timestamp float64 `json:"Timestamp"`
    67  	Value     float64 `json:"Value"`
    68  }
    69  
    70  type SBatchQueryMetricDataInput struct {
    71  	MetricName string               `json:"MetricName"`
    72  	Namespace  string               `json:"Namespace"`
    73  	Metrics    []SQcMetricDimension `json:"Metrics"`
    74  	StartTime  int64                `json:"StartTime"`
    75  	EndTime    int64                `json:"EndTime"`
    76  	Period     string               `json:"Period"`
    77  }
    78  
    79  func (self *SQcloudClient) metricsRequest(action string, params map[string]string) (jsonutils.JSONObject, error) {
    80  	cli, err := self.getDefaultClient(params)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  	return monitorRequest(cli, action, params, self.cpcfg.UpdatePermission, self.debug)
    85  }
    86  
    87  func (self *SQcloudClient) GetMonitorData(ns string, name string, since time.Time, until time.Time, regionId string, dimensionName string, resIds []string) ([]SDataPoint, error) {
    88  	params := make(map[string]string)
    89  	params["Region"] = regionId
    90  	params["MetricName"] = name
    91  	params["Namespace"] = ns
    92  	params["StartTime"] = since.Format(timeutils.IsoTimeFormat)
    93  	params["EndTime"] = until.Format(timeutils.IsoTimeFormat)
    94  	for idx, resId := range resIds {
    95  		params[fmt.Sprintf("Instances.%d.Dimensions.0.Name", idx)] = dimensionName
    96  		params[fmt.Sprintf("Instances.%d.Dimensions.0.Value", idx)] = resId
    97  	}
    98  	body, err := self.metricsRequest("GetMonitorData", params)
    99  	if err != nil {
   100  		return nil, errors.Wrapf(err, "MetricRequest for %s", resIds)
   101  	}
   102  	ret := []SDataPoint{}
   103  	err = body.Unmarshal(&ret, "DataPoints")
   104  	if err != nil {
   105  		return nil, errors.Wrap(err, "resp.Unmarshal")
   106  	}
   107  	return ret, nil
   108  }
   109  
   110  func (self *SQcloudClient) GetK8sMonitorData(ns, name, resourceId string, since time.Time, until time.Time, regionId string) ([]SK8SDataPoint, error) {
   111  	params := make(map[string]string)
   112  	params["Module"] = "monitor"
   113  	params["Region"] = regionId
   114  	params["MetricNames.0"] = name
   115  	params["Namespace"] = ns
   116  	params["Period"] = "60"
   117  	params["StartTime"] = since.Format(timeutils.IsoTimeFormat)
   118  	params["EndTime"] = until.Format(timeutils.IsoTimeFormat)
   119  	params["Conditions.0.Key"] = "tke_cluster_instance_id"
   120  	params["Conditions.0.Operator"] = "in"
   121  	params["Conditions.0.Value.0"] = resourceId
   122  	body, err := self.metricsRequest("DescribeStatisticData", params)
   123  	if err != nil {
   124  		return nil, errors.Wrap(err, "region.MetricRequest")
   125  	}
   126  	ret := []SK8SDataPoint{}
   127  	return ret, body.Unmarshal(&ret, "Data")
   128  }
   129  
   130  func (self *SQcloudClient) GetMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
   131  	switch opts.ResourceType {
   132  	case cloudprovider.METRIC_RESOURCE_TYPE_SERVER:
   133  		return self.GetEcsMetrics(opts)
   134  	case cloudprovider.METRIC_RESOURCE_TYPE_REDIS:
   135  		return self.GetRedisMetrics(opts)
   136  	case cloudprovider.METRIC_RESOURCE_TYPE_RDS:
   137  		return self.GetRdsMetrics(opts)
   138  	case cloudprovider.METRIC_RESOURCE_TYPE_K8S:
   139  		return self.GetK8sMetrics(opts)
   140  	default:
   141  		return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "%s", opts.ResourceType)
   142  	}
   143  }
   144  
   145  func (self *SQcloudClient) GetEcsMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
   146  	ret := []cloudprovider.MetricValues{}
   147  	for metricType, metricNames := range map[cloudprovider.TMetricType]map[string]string{
   148  		cloudprovider.VM_METRIC_TYPE_CPU_USAGE: {
   149  			"CPUUsage": "",
   150  		},
   151  		cloudprovider.VM_METRIC_TYPE_MEM_USAGE: {
   152  			"MemUsage": "",
   153  		},
   154  		cloudprovider.VM_METRIC_TYPE_NET_BPS_TX: {
   155  			"lanOuttraffic": cloudprovider.METRIC_TAG_NET_TYPE + ":" + cloudprovider.METRIC_TAG_NET_TYPE_INTRANET,
   156  			"WanOuttraffic": cloudprovider.METRIC_TAG_NET_TYPE + ":" + cloudprovider.METRIC_TAG_NET_TYPE_INTERNET,
   157  		},
   158  		cloudprovider.VM_METRIC_TYPE_NET_BPS_RX: {
   159  			"lanIntraffic": cloudprovider.METRIC_TAG_NET_TYPE + ":" + cloudprovider.METRIC_TAG_NET_TYPE_INTRANET,
   160  			"WanIntraffic": cloudprovider.METRIC_TAG_NET_TYPE + ":" + cloudprovider.METRIC_TAG_NET_TYPE_INTERNET,
   161  		},
   162  	} {
   163  		for metricName, tag := range metricNames {
   164  			metrics, err := self.GetMonitorData("QCE/CVM", metricName, opts.StartTime, opts.EndTime, opts.RegionExtId, "InstanceId", opts.ResourceIds)
   165  			if err != nil {
   166  				log.Errorf("GetMonitorData error: %v", err)
   167  				continue
   168  			}
   169  			for i := range metrics {
   170  				metric := cloudprovider.MetricValues{}
   171  				if len(metrics[i].Dimensions) < 1 || metrics[i].Dimensions[0].Name != "InstanceId" {
   172  					continue
   173  				}
   174  				metric.Id = metrics[i].Dimensions[0].Value
   175  				metric.MetricType = metricType
   176  				metricValue := cloudprovider.MetricValue{}
   177  				metricValue.Tags = map[string]string{}
   178  				idx := strings.Index(tag, ":")
   179  				if idx > 0 {
   180  					metricValue.Tags[tag[:idx]] = tag[idx+1:]
   181  				}
   182  				if len(metrics[i].Timestamps) == 0 {
   183  					continue
   184  				}
   185  				for j := range metrics[i].Timestamps {
   186  					metricValue.Value = metrics[i].Values[j]
   187  					if strings.Contains(metricName, "traffic") { //Mbps
   188  						metricValue.Value *= 1024 * 1024
   189  					}
   190  					metricValue.Timestamp = time.Unix(int64(metrics[i].Timestamps[j]), 0)
   191  					metric.Values = append(metric.Values, metricValue)
   192  				}
   193  				ret = append(ret, metric)
   194  			}
   195  		}
   196  	}
   197  	return ret, nil
   198  }
   199  
   200  func (self *SQcloudClient) GetRedisMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
   201  	ret := []cloudprovider.MetricValues{}
   202  	for metricType, metricNames := range map[cloudprovider.TMetricType]map[string]string{
   203  		cloudprovider.REDIS_METRIC_TYPE_CPU_USAGE: {
   204  			"CpuUsMin": "",
   205  		},
   206  		cloudprovider.REDIS_METRIC_TYPE_MEM_USAGE: {
   207  			"StorageUsMin": "",
   208  		},
   209  		cloudprovider.REDIS_METRIC_TYPE_NET_BPS_RX: {
   210  			"InFlowMin": "",
   211  		},
   212  		cloudprovider.REDIS_METRIC_TYPE_NET_BPS_TX: {
   213  			"OutFlowMin": "",
   214  		},
   215  		cloudprovider.REDIS_METRIC_TYPE_USED_CONN: {
   216  			"ConnectionsMin": "",
   217  		},
   218  		cloudprovider.REDIS_METRIC_TYPE_OPT_SES: {
   219  			"QpsMin": "",
   220  		},
   221  		cloudprovider.REDIS_METRIC_TYPE_CACHE_KEYS: {
   222  			"KeysMin": "",
   223  		},
   224  		cloudprovider.REDIS_METRIC_TYPE_CACHE_EXP_KEYS: {
   225  			"ExpiredKeysMin": "",
   226  		},
   227  		cloudprovider.REDIS_METRIC_TYPE_DATA_MEM_USAGE: {
   228  			"StorageMin": "",
   229  		},
   230  	} {
   231  		for metricName, tag := range metricNames {
   232  			metrics, err := self.GetMonitorData("QCE/REDIS", metricName, opts.StartTime, opts.EndTime, opts.RegionExtId, "instanceid", opts.ResourceIds)
   233  			if err != nil {
   234  				log.Errorf("GetMonitorData error: %v", err)
   235  				continue
   236  			}
   237  			for i := range metrics {
   238  				metric := cloudprovider.MetricValues{}
   239  				if len(metrics[i].Dimensions) < 1 || metrics[i].Dimensions[0].Name != "instanceid" {
   240  					continue
   241  				}
   242  				metric.Id = metrics[i].Dimensions[0].Value
   243  				metric.MetricType = metricType
   244  				metricValue := cloudprovider.MetricValue{}
   245  				metricValue.Tags = map[string]string{}
   246  				idx := strings.Index(tag, ":")
   247  				if idx > 0 {
   248  					metricValue.Tags[tag[:idx]] = tag[idx+1:]
   249  				}
   250  				if len(metrics[i].Timestamps) == 0 {
   251  					continue
   252  				}
   253  				for j := range metrics[i].Timestamps {
   254  					metricValue.Value = metrics[i].Values[j]
   255  					metricValue.Timestamp = time.Unix(int64(metrics[i].Timestamps[j]), 0)
   256  					metric.Values = append(metric.Values, metricValue)
   257  				}
   258  				ret = append(ret, metric)
   259  			}
   260  		}
   261  	}
   262  	return ret, nil
   263  }
   264  
   265  func (self *SQcloudClient) GetRdsMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
   266  	ret := []cloudprovider.MetricValues{}
   267  	for metricType, metricNames := range map[cloudprovider.TMetricType]map[string]string{
   268  		cloudprovider.RDS_METRIC_TYPE_CPU_USAGE: {
   269  			"CPUUseRate": "",
   270  		},
   271  		cloudprovider.RDS_METRIC_TYPE_MEM_USAGE: {
   272  			"MemoryUseRate": "",
   273  		},
   274  		cloudprovider.RDS_METRIC_TYPE_NET_BPS_TX: {
   275  			"BytesSent": cloudprovider.METRIC_TAG_NET_TYPE + ":" + cloudprovider.METRIC_TAG_NET_TYPE_INTRANET,
   276  		},
   277  		cloudprovider.RDS_METRIC_TYPE_NET_BPS_RX: {
   278  			"BytesReceived": cloudprovider.METRIC_TAG_NET_TYPE + ":" + cloudprovider.METRIC_TAG_NET_TYPE_INTRANET,
   279  		},
   280  		cloudprovider.RDS_METRIC_TYPE_DISK_USAGE: {
   281  			"VolumeRate": "",
   282  		},
   283  		cloudprovider.RDS_METRIC_TYPE_CONN_COUNT: {
   284  			"ThreadsConnected": "",
   285  		},
   286  		cloudprovider.RDS_METRIC_TYPE_CONN_USAGE: {
   287  			"ConnectionUseRate": "",
   288  		},
   289  		cloudprovider.RDS_METRIC_TYPE_QPS: {
   290  			"QPS": "",
   291  		},
   292  		cloudprovider.RDS_METRIC_TYPE_TPS: {
   293  			"TPS": "",
   294  		},
   295  		cloudprovider.RDS_METRIC_TYPE_INNODB_READ_BPS: {
   296  			"InnodbDataRead": "",
   297  		},
   298  		cloudprovider.RDS_METRIC_TYPE_INNODB_WRITE_BPS: {
   299  			"InnodbDataWritten": "",
   300  		},
   301  	} {
   302  		for metricName, tag := range metricNames {
   303  			metrics, err := self.GetMonitorData("QCE/CDB", metricName, opts.StartTime, opts.EndTime, opts.RegionExtId, "InstanceId", opts.ResourceIds)
   304  			if err != nil {
   305  				log.Errorf("GetMonitorData error: %v", err)
   306  				continue
   307  			}
   308  			for i := range metrics {
   309  				metric := cloudprovider.MetricValues{}
   310  				if len(metrics[i].Dimensions) < 1 || metrics[i].Dimensions[0].Name != "InstanceId" {
   311  					continue
   312  				}
   313  				metric.Id = metrics[i].Dimensions[0].Value
   314  				metric.MetricType = metricType
   315  				metricValue := cloudprovider.MetricValue{}
   316  				metricValue.Tags = map[string]string{}
   317  				idx := strings.Index(tag, ":")
   318  				if idx > 0 {
   319  					metricValue.Tags[tag[:idx]] = tag[idx+1:]
   320  				}
   321  				if len(metrics[i].Timestamps) == 0 {
   322  					continue
   323  				}
   324  				for j := range metrics[i].Timestamps {
   325  					metricValue.Value = metrics[i].Values[j]
   326  					metricValue.Timestamp = time.Unix(int64(metrics[i].Timestamps[j]), 0)
   327  					metric.Values = append(metric.Values, metricValue)
   328  				}
   329  				ret = append(ret, metric)
   330  			}
   331  		}
   332  	}
   333  	return ret, nil
   334  }
   335  
   336  func (self *SQcloudClient) GetK8sMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
   337  	ret := []cloudprovider.MetricValues{}
   338  	for metricType, metricName := range map[cloudprovider.TMetricType]string{
   339  		cloudprovider.K8S_NODE_METRIC_TYPE_CPU_USAGE: "K8sNodeCpuUsage",
   340  		cloudprovider.K8S_NODE_METRIC_TYPE_MEM_USAGE: "K8sNodeMemUsage",
   341  	} {
   342  		metrics, err := self.GetK8sMonitorData("QCE/TKE2", metricName, opts.ResourceId, opts.StartTime, opts.EndTime, opts.RegionExtId)
   343  		if err != nil {
   344  			log.Errorf("GetMonitorData error: %v", err)
   345  			continue
   346  		}
   347  		for i := range metrics {
   348  			for _, point := range metrics[i].Points {
   349  				tags := map[string]string{}
   350  				for _, dim := range point.Dimensions {
   351  					if dim.Name == "node" {
   352  						tags[cloudprovider.METRIC_TAG_NODE] = dim.Value
   353  					}
   354  				}
   355  				metric := cloudprovider.MetricValues{}
   356  				metric.Id = opts.ResourceId
   357  				metric.MetricType = metricType
   358  				for _, value := range point.Values {
   359  					metric.Values = append(metric.Values, cloudprovider.MetricValue{
   360  						Value:     value.Value,
   361  						Timestamp: time.Unix(int64(value.Timestamp), 0),
   362  						Tags:      tags,
   363  					})
   364  				}
   365  				if len(metric.Values) > 0 {
   366  					ret = append(ret, metric)
   367  				}
   368  			}
   369  		}
   370  	}
   371  	return ret, nil
   372  }