yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/aliyun/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 aliyun 16 17 import ( 18 "fmt" 19 "strconv" 20 "strings" 21 "time" 22 23 "yunion.io/x/jsonutils" 24 "yunion.io/x/log" 25 "yunion.io/x/pkg/errors" 26 27 "yunion.io/x/cloudmux/pkg/cloudprovider" 28 ) 29 30 const ( 31 ALIYUN_API_VERSION_METRICS = "2019-01-01" 32 ) 33 34 func (r *SRegion) metricsRequest(action string, params map[string]string) (jsonutils.JSONObject, error) { 35 return r.client.metricsRequest(action, params) 36 } 37 38 func (self *SAliyunClient) metricsRequest(action string, params map[string]string) (jsonutils.JSONObject, error) { 39 client, err := self.getSdkClient("") 40 if err != nil { 41 return nil, errors.Wrap(err, "self.getSdkClient") 42 } 43 return jsonRequest(client, "metrics.aliyuncs.com", ALIYUN_API_VERSION_METRICS, action, params, self.debug) 44 } 45 46 type SResourceLabel struct { 47 Name string `json:"name"` 48 Value string `json:"value"` 49 } 50 51 type SResource struct { 52 Description string `json:"Description"` 53 Labels string `json:"Labels"` 54 Namespace string `json:"Namespace"` 55 } 56 57 func (r *SRegion) DescribeProjectMeta(limit, offset int) (int, []SResource, error) { 58 params := make(map[string]string) 59 if limit <= 0 { 60 limit = 30 61 } 62 params["PageSize"] = strconv.FormatInt(int64(limit), 10) 63 if offset > 0 { 64 pageNum := (offset / limit) + 1 65 params["PageNumber"] = strconv.FormatInt(int64(pageNum), 10) 66 } 67 body, err := r.metricsRequest("DescribeProjectMeta", params) 68 if err != nil { 69 return 0, nil, errors.Wrap(err, "r.metricsRequest DescribeProjectMeta") 70 } 71 total, _ := body.Int("Total") 72 res := make([]SResource, 0) 73 err = body.Unmarshal(&res, "Resources", "Resource") 74 if err != nil { 75 return 0, nil, errors.Wrap(err, "body.Unmarshal Resources Resource") 76 } 77 return int(total), res, nil 78 } 79 80 func (r *SRegion) FetchNamespaces() ([]SResource, error) { 81 resources := make([]SResource, 0) 82 total := -1 83 for total < 0 || len(resources) < total { 84 ntotal, res, err := r.DescribeProjectMeta(1000, len(resources)) 85 if err != nil { 86 return nil, errors.Wrap(err, "r.DescribeProjectMeta") 87 } 88 if len(res) == 0 { 89 break 90 } 91 resources = append(resources, res...) 92 total = ntotal 93 } 94 return resources, nil 95 } 96 97 type SMetricMeta struct { 98 Description string `json:"Description"` 99 MetricName string `json:"MetricName"` 100 Statistics string `json:"Statistics"` 101 Labels string `json:"Labels"` 102 Dimensions string `json:"Dimensions"` 103 Namespace string `json:"Namespace"` 104 Periods string `json:"Periods"` 105 Unit string `json:"Unit"` 106 } 107 108 func (r *SRegion) DescribeMetricMetaList(ns string, limit, offset int) (int, []SMetricMeta, error) { 109 params := make(map[string]string) 110 if limit <= 0 { 111 limit = 30 112 } 113 params["Namespace"] = ns 114 params["PageSize"] = strconv.FormatInt(int64(limit), 10) 115 if offset > 0 { 116 pageNum := (offset / limit) + 1 117 params["PageNumber"] = strconv.FormatInt(int64(pageNum), 10) 118 } 119 body, err := r.metricsRequest("DescribeMetricMetaList", params) 120 if err != nil { 121 return 0, nil, errors.Wrap(err, "r.metricsRequest DescribeMetricMetaList") 122 } 123 total, _ := body.Int("TotalCount") 124 res := make([]SMetricMeta, 0) 125 err = body.Unmarshal(&res, "Resources", "Resource") 126 if err != nil { 127 return 0, nil, errors.Wrap(err, "body.Unmarshal Resources Resource") 128 } 129 return int(total), res, nil 130 } 131 132 func (r *SRegion) FetchMetrics(ns string) ([]SMetricMeta, error) { 133 metrics := make([]SMetricMeta, 0) 134 total := -1 135 for total < 0 || len(metrics) < total { 136 ntotal, res, err := r.DescribeMetricMetaList(ns, 1000, len(metrics)) 137 if err != nil { 138 return nil, errors.Wrap(err, "r.DescribeMetricMetaList") 139 } 140 if len(res) == 0 { 141 break 142 } 143 metrics = append(metrics, res...) 144 total = ntotal 145 } 146 return metrics, nil 147 } 148 149 func (r *SRegion) DescribeMetricList(name string, ns string, since time.Time, until time.Time, nextToken string, dimensions []SResourceLabel) ([]jsonutils.JSONObject, string, error) { 150 params := make(map[string]string) 151 params["MetricName"] = name 152 params["Namespace"] = ns 153 params["Length"] = "2000" 154 if len(nextToken) > 0 { 155 params["NextToken"] = nextToken 156 } 157 if !since.IsZero() { 158 params["StartTime"] = strconv.FormatInt(since.Unix()*1000, 10) 159 } 160 if !until.IsZero() { 161 params["EndTime"] = strconv.FormatInt(until.Unix()*1000, 10) 162 } 163 if len(dimensions) > 0 { 164 for _, dimension := range dimensions { 165 params[dimension.Name] = dimension.Value 166 } 167 } 168 body, err := r.metricsRequest("DescribeMetricList", params) 169 if err != nil { 170 return nil, "", errors.Wrap(err, "region.MetricRequest") 171 } 172 nToken, _ := body.GetString("NextToken") 173 dataStr, _ := body.GetString("Datapoints") 174 if len(dataStr) == 0 { 175 return nil, "", nil 176 } 177 dataJson, err := jsonutils.ParseString(dataStr) 178 if err != nil { 179 return nil, "", errors.Wrap(err, "jsonutils.ParseString") 180 } 181 dataArray, err := dataJson.GetArray() 182 if err != nil { 183 return nil, "", errors.Wrap(err, "dataJson.GetArray") 184 } 185 return dataArray, nToken, nil 186 } 187 188 type MetricData struct { 189 Timestamp int64 190 BucketName string 191 InstanceId string 192 UserId string 193 Value float64 194 Average float64 195 Minimum float64 196 Maximum float64 197 198 Diskname string 199 Device string 200 201 // k8s 202 Cluster string 203 Node string 204 Pod string 205 } 206 207 func (d MetricData) GetValue() float64 { 208 if d.Average > 0 { 209 return d.Average 210 } 211 if d.Maximum > 0 { 212 return d.Maximum 213 } 214 if d.Minimum > 0 { 215 return d.Minimum 216 } 217 if d.Value > 0 { 218 return d.Value 219 } 220 return 0.0 221 } 222 223 func (d MetricData) GetTags() map[string]string { 224 ret := map[string]string{} 225 if len(d.Device) > 0 { 226 ret[cloudprovider.METRIC_TAG_DEVICE] = fmt.Sprintf("%s(%s)", d.Device, d.Diskname) 227 } 228 return ret 229 } 230 231 func (self *SAliyunClient) ListMetrics(ns, metricName string, start, end time.Time) ([]MetricData, error) { 232 result := []MetricData{} 233 nextToken := "" 234 for { 235 part, next, err := self.listMetrics(ns, metricName, nextToken, start, end) 236 if err != nil { 237 return nil, errors.Wrap(err, "listMetrics") 238 } 239 result = append(result, part...) 240 if len(next) == 0 { 241 break 242 } 243 nextToken = next 244 } 245 return result, nil 246 } 247 248 func (self *SAliyunClient) listMetrics(ns, metricName, nextToken string, start, end time.Time) ([]MetricData, string, error) { 249 params := make(map[string]string) 250 params["MetricName"] = metricName 251 params["Namespace"] = ns 252 params["Length"] = "2000" 253 if len(nextToken) > 0 { 254 params["NextToken"] = nextToken 255 } 256 params["StartTime"] = fmt.Sprintf("%d", start.UnixMilli()) 257 params["EndTime"] = fmt.Sprintf("%d", end.UnixMilli()) 258 resp, err := self.metricsRequest("DescribeMetricList", params) 259 if err != nil { 260 return nil, "", errors.Wrap(err, "DescribeMetricList") 261 } 262 ret := struct { 263 NextToken string 264 Datapoints string 265 }{} 266 err = resp.Unmarshal(&ret) 267 if err != nil { 268 return nil, "", errors.Wrapf(err, "resp.Unmarshal") 269 } 270 obj, err := jsonutils.ParseString(ret.Datapoints) 271 if err != nil { 272 return nil, "", errors.Wrap(err, "jsonutils.ParseString") 273 } 274 result := []MetricData{} 275 err = obj.Unmarshal(&result) 276 if err != nil { 277 return nil, "", errors.Wrapf(err, "obj.Unmarshal") 278 } 279 return result, ret.NextToken, nil 280 } 281 282 func (r *SRegion) FetchMetricData(name string, ns string, since time.Time, until time.Time) ([]jsonutils.JSONObject, error) { 283 data := make([]jsonutils.JSONObject, 0) 284 nextToken := "" 285 for { 286 datArray, next, err := r.DescribeMetricList(name, ns, since, until, nextToken, nil) 287 if err != nil { 288 return nil, errors.Wrap(err, "r.DescribeMetricList") 289 } 290 data = append(data, datArray...) 291 if len(next) == 0 { 292 break 293 } 294 nextToken = next 295 } 296 return data, nil 297 } 298 299 func (self *SAliyunClient) GetMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) { 300 switch opts.ResourceType { 301 case cloudprovider.METRIC_RESOURCE_TYPE_SERVER: 302 return self.GetEcsMetrics(opts) 303 case cloudprovider.METRIC_RESOURCE_TYPE_BUCKET: 304 return self.GetOssMetrics(opts) 305 case cloudprovider.METRIC_RESOURCE_TYPE_REDIS: 306 return self.GetRedisMetrics(opts) 307 case cloudprovider.METRIC_RESOURCE_TYPE_RDS: 308 return self.GetRdsMetrics(opts) 309 case cloudprovider.METRIC_RESOURCE_TYPE_LB: 310 return self.GetElbMetrics(opts) 311 case cloudprovider.METRIC_RESOURCE_TYPE_K8S: 312 return self.GetK8sMetrics(opts) 313 default: 314 return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "%s", opts.ResourceType) 315 } 316 } 317 318 func (self *SAliyunClient) GetEcsMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) { 319 metricTags, tagKey := map[string]string{}, "" 320 switch opts.MetricType { 321 case cloudprovider.VM_METRIC_TYPE_CPU_USAGE: 322 metricTags = map[string]string{ 323 "CPUUtilization": "", 324 } 325 case cloudprovider.VM_METRIC_TYPE_NET_BPS_RX: 326 metricTags = map[string]string{ 327 "InternetInRate": cloudprovider.METRIC_TAG_NET_TYPE_INTERNET, 328 "IntranetInRate": cloudprovider.METRIC_TAG_NET_TYPE_INTRANET, 329 } 330 tagKey = cloudprovider.METRIC_TAG_NET_TYPE 331 case cloudprovider.VM_METRIC_TYPE_NET_BPS_TX: 332 metricTags = map[string]string{ 333 "InternetOutRate": cloudprovider.METRIC_TAG_NET_TYPE_INTERNET, 334 "IntranetOutRate": cloudprovider.METRIC_TAG_NET_TYPE_INTRANET, 335 } 336 tagKey = cloudprovider.METRIC_TAG_NET_TYPE 337 case cloudprovider.VM_METRIC_TYPE_DISK_IO_READ_BPS: 338 metricTags = map[string]string{ 339 "DiskReadBPS": "", 340 } 341 case cloudprovider.VM_METRIC_TYPE_DISK_IO_WRITE_BPS: 342 metricTags = map[string]string{ 343 "DiskWriteBPS": "", 344 } 345 case cloudprovider.VM_METRIC_TYPE_DISK_IO_READ_IOPS: 346 metricTags = map[string]string{ 347 "DiskReadIOPS": "", 348 } 349 case cloudprovider.VM_METRIC_TYPE_DISK_IO_WRITE_IOPS: 350 metricTags = map[string]string{ 351 "DiskWriteIOPS": "", 352 } 353 case cloudprovider.VM_METRIC_TYPE_MEM_USAGE: 354 metricTags = map[string]string{ 355 "memory_usedutilization": "", 356 } 357 case cloudprovider.VM_METRIC_TYPE_DISK_USAGE: 358 metricTags = map[string]string{ 359 "diskusage_utilization": "", 360 } 361 default: 362 return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "%s", opts.MetricType) 363 } 364 ret := []cloudprovider.MetricValues{} 365 for metric, tag := range metricTags { 366 result, err := self.ListMetrics("acs_ecs_dashboard", metric, opts.StartTime, opts.EndTime) 367 if err != nil { 368 log.Errorf("ListMetric(%s) error: %v", metric, err) 369 continue 370 } 371 tags := map[string]string{} 372 if len(tag) > 0 && len(tagKey) > 0 { 373 tags[tagKey] = tag 374 } 375 for i := range result { 376 dataTag := result[i].GetTags() 377 for k, v := range tags { 378 dataTag[k] = v 379 } 380 ret = append(ret, cloudprovider.MetricValues{ 381 Id: result[i].InstanceId, 382 MetricType: opts.MetricType, 383 Values: []cloudprovider.MetricValue{ 384 { 385 Timestamp: time.UnixMilli(result[i].Timestamp), 386 Value: result[i].GetValue(), 387 Tags: dataTag, 388 }, 389 }, 390 }) 391 } 392 } 393 return ret, nil 394 } 395 396 func (self *SAliyunClient) GetOssMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) { 397 metricTags, tagKey := map[string]string{}, "" 398 switch opts.MetricType { 399 case cloudprovider.BUCKET_METRIC_TYPE_LATECY: 400 metricTags = map[string]string{ 401 "GetObjectE2eLatency": cloudprovider.METRIC_TAG_REQUST_GET, 402 "PostObjectE2eLatency": cloudprovider.METRIC_TAG_REQUST_GET, 403 } 404 tagKey = cloudprovider.METRIC_TAG_REQUST 405 case cloudprovider.BUCKET_METRIC_TYPE_NET_BPS_TX: 406 metricTags = map[string]string{ 407 "InternetSend": cloudprovider.METRIC_TAG_NET_TYPE_INTERNET, 408 "IntranetSend": cloudprovider.METRIC_TAG_NET_TYPE_INTRANET, 409 } 410 tagKey = cloudprovider.METRIC_TAG_REQUST 411 case cloudprovider.BUCKET_METRIC_TYPE_NET_BPS_RX: 412 metricTags = map[string]string{ 413 "InternetRecv": cloudprovider.METRIC_TAG_NET_TYPE_INTERNET, 414 "IntranetRecv": cloudprovider.METRIC_TAG_NET_TYPE_INTRANET, 415 } 416 tagKey = cloudprovider.METRIC_TAG_NET_TYPE 417 case cloudprovider.BUCKET_METRYC_TYPE_REQ_COUNT: 418 metricTags = map[string]string{ 419 "GetObjectCount": cloudprovider.METRIC_TAG_REQUST_GET, 420 "PostObjectCount": cloudprovider.METRIC_TAG_REQUST_POST, 421 "ServerErrorCount": cloudprovider.METRIC_TAG_REQUST_5XX, 422 } 423 tagKey = cloudprovider.METRIC_TAG_REQUST 424 default: 425 return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "%s", opts.MetricType) 426 } 427 ret := []cloudprovider.MetricValues{} 428 for metric, tag := range metricTags { 429 result, err := self.ListMetrics("acs_oss_dashboard", metric, opts.StartTime, opts.EndTime) 430 if err != nil { 431 log.Errorf("ListMetric(%s) error: %v", metric, err) 432 continue 433 } 434 for i := range result { 435 ret = append(ret, cloudprovider.MetricValues{ 436 Id: result[i].BucketName, 437 MetricType: opts.MetricType, 438 Values: []cloudprovider.MetricValue{ 439 { 440 Timestamp: time.UnixMilli(result[i].Timestamp), 441 Value: result[i].GetValue(), 442 Tags: map[string]string{ 443 tagKey: tag, 444 }, 445 }, 446 }, 447 }) 448 } 449 } 450 return ret, nil 451 } 452 453 func (self *SAliyunClient) GetRedisMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) { 454 metrics := map[cloudprovider.TMetricType]string{ 455 cloudprovider.REDIS_METRIC_TYPE_CPU_USAGE: "CpuUsage", 456 cloudprovider.REDIS_METRIC_TYPE_MEM_USAGE: "MemoryUsage", 457 cloudprovider.REDIS_METRIC_TYPE_NET_BPS_RX: "IntranetIn", 458 cloudprovider.REDIS_METRIC_TYPE_NET_BPS_TX: "IntranetOut", 459 cloudprovider.REDIS_METRIC_TYPE_USED_CONN: "UsedConnection", 460 cloudprovider.REDIS_METRIC_TYPE_OPT_SES: "UsedQPS", 461 cloudprovider.REDIS_METRIC_TYPE_CACHE_KEYS: "StandardKeys", 462 cloudprovider.REDIS_METRIC_TYPE_DATA_MEM_USAGE: "UsedMemory", 463 } 464 metric, ok := metrics[opts.MetricType] 465 if !ok { 466 return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "%s", opts.MetricType) 467 } 468 ret := []cloudprovider.MetricValues{} 469 result, err := self.ListMetrics("acs_kvstore", metric, opts.StartTime, opts.EndTime) 470 if err != nil { 471 return nil, errors.Wrapf(err, "ListMetric(%s)", metric) 472 } 473 for i := range result { 474 tags := map[string]string{} 475 if strings.Contains(result[i].InstanceId, "-db-") { 476 tags[cloudprovider.METRIC_TAG_NODE] = result[i].InstanceId 477 idx := strings.Index(result[i].InstanceId, "-db-") 478 result[i].InstanceId = result[i].InstanceId[:idx] 479 } 480 value := cloudprovider.MetricValues{ 481 Id: result[i].InstanceId, 482 MetricType: opts.MetricType, 483 Values: []cloudprovider.MetricValue{ 484 { 485 Timestamp: time.UnixMilli(result[i].Timestamp), 486 Value: result[i].GetValue(), 487 Tags: tags, 488 }, 489 }, 490 } 491 ret = append(ret, value) 492 } 493 return ret, nil 494 } 495 496 func (self *SAliyunClient) GetRdsMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) { 497 metricTags := map[string]string{} 498 switch opts.MetricType { 499 case cloudprovider.RDS_METRIC_TYPE_CPU_USAGE: 500 metricTags = map[string]string{ 501 "CpuUsage": "", 502 } 503 case cloudprovider.RDS_METRIC_TYPE_MEM_USAGE: 504 metricTags = map[string]string{ 505 "MemoryUsage": "", 506 } 507 case cloudprovider.RDS_METRIC_TYPE_NET_BPS_RX: 508 metricTags = map[string]string{ 509 "MySQL_NetworkInNew": "", 510 "SQLServer_NetworkInNew": "", 511 } 512 case cloudprovider.RDS_METRIC_TYPE_NET_BPS_TX: 513 metricTags = map[string]string{ 514 "MySQL_NetworkOutNew": "", 515 "SQLServer_NetworkOutNew": "", 516 } 517 case cloudprovider.RDS_METRIC_TYPE_DISK_USAGE: 518 metricTags = map[string]string{ 519 "DiskUsage": "", 520 } 521 case cloudprovider.RDS_METRIC_TYPE_CONN_USAGE: 522 metricTags = map[string]string{ 523 "ConnectionUsage": "", 524 } 525 default: 526 return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "%s", opts.MetricType) 527 } 528 ret := []cloudprovider.MetricValues{} 529 for metric := range metricTags { 530 result, err := self.ListMetrics("acs_rds_dashboard", metric, opts.StartTime, opts.EndTime) 531 if err != nil { 532 log.Errorf("ListMetric(%s) error: %v", metric, err) 533 continue 534 } 535 for i := range result { 536 ret = append(ret, cloudprovider.MetricValues{ 537 Id: result[i].InstanceId, 538 MetricType: opts.MetricType, 539 Values: []cloudprovider.MetricValue{ 540 { 541 Timestamp: time.UnixMilli(result[i].Timestamp), 542 Value: result[i].GetValue(), 543 }, 544 }, 545 }) 546 } 547 } 548 return ret, nil 549 } 550 551 func (self *SAliyunClient) GetElbMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) { 552 metricTags, tagKey := map[string]string{}, "" 553 switch opts.MetricType { 554 case cloudprovider.LB_METRIC_TYPE_NET_BPS_RX: 555 metricTags = map[string]string{ 556 "InstanceTrafficRX": "", 557 } 558 case cloudprovider.LB_METRIC_TYPE_NET_BPS_TX: 559 metricTags = map[string]string{ 560 "InstanceTrafficTX": "", 561 } 562 case cloudprovider.LB_METRIC_TYPE_HRSP_COUNT: 563 metricTags = map[string]string{ 564 "InstanceStatusCode2xx": cloudprovider.METRIC_TAG_REQUST_2XX, 565 "InstanceStatusCode3xx": cloudprovider.METRIC_TAG_REQUST_3XX, 566 "InstanceStatusCode4xx": cloudprovider.METRIC_TAG_REQUST_4XX, 567 "InstanceStatusCode5xx": cloudprovider.METRIC_TAG_REQUST_5XX, 568 } 569 tagKey = cloudprovider.METRIC_TAG_REQUST 570 default: 571 return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "%s", opts.MetricType) 572 } 573 ret := []cloudprovider.MetricValues{} 574 for metric, tag := range metricTags { 575 result, err := self.ListMetrics("acs_slb_dashboard", metric, opts.StartTime, opts.EndTime) 576 if err != nil { 577 log.Errorf("ListMetric(%s) error: %v", metric, err) 578 continue 579 } 580 tags := map[string]string{} 581 if len(tag) > 0 && len(tagKey) > 0 { 582 tags[tagKey] = tag 583 } 584 for i := range result { 585 ret = append(ret, cloudprovider.MetricValues{ 586 Id: result[i].InstanceId, 587 MetricType: opts.MetricType, 588 Values: []cloudprovider.MetricValue{ 589 { 590 Timestamp: time.UnixMilli(result[i].Timestamp), 591 Value: result[i].GetValue(), 592 Tags: tags, 593 }, 594 }, 595 }) 596 } 597 } 598 return ret, nil 599 } 600 601 func (self *SAliyunClient) GetK8sMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) { 602 metricName := "" 603 switch opts.MetricType { 604 case cloudprovider.K8S_NODE_METRIC_TYPE_CPU_USAGE: 605 metricName = "node.cpu.utilization" 606 case cloudprovider.K8S_NODE_METRIC_TYPE_MEM_USAGE: 607 metricName = "node.memory.utilization" 608 default: 609 return nil, errors.Wrapf(cloudprovider.ErrNotImplemented, "%s", opts.MetricType) 610 } 611 result, err := self.ListMetrics("acs_k8s", metricName, opts.StartTime, opts.EndTime) 612 if err != nil { 613 return nil, errors.Wrapf(err, "ListMetrics(%s)", metricName) 614 } 615 ret := []cloudprovider.MetricValues{} 616 for i := range result { 617 tags := map[string]string{} 618 if len(result[i].Node) > 0 { 619 tags[cloudprovider.METRIC_TAG_NODE] = result[i].Node 620 } 621 ret = append(ret, cloudprovider.MetricValues{ 622 Id: result[i].Cluster, 623 MetricType: opts.MetricType, 624 Values: []cloudprovider.MetricValue{ 625 { 626 Timestamp: time.UnixMilli(result[i].Timestamp), 627 Value: result[i].GetValue(), 628 Tags: tags, 629 }, 630 }, 631 }) 632 } 633 return ret, nil 634 }