Skip to content

Commit d690de0

Browse files
authored
[aws][fix] S3 metrics for all storage types (#2058)
1 parent 29017fb commit d690de0

File tree

5 files changed

+68
-17
lines changed

5 files changed

+68
-17
lines changed

plugins/aws/fix_plugin_aws/resource/cloudwatch.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,7 @@ class AwsCloudwatchQuery:
418418
metric_id: str
419419
stat: str = "Sum"
420420
unit: str = "Count"
421+
fix_metric_name: Optional[str] = None # Override the default metric name. The name is taken AS IS.
421422

422423
def to_json(self) -> Json:
423424
return {
@@ -437,13 +438,15 @@ def to_json(self) -> Json:
437438

438439
@staticmethod
439440
def create(
441+
*,
440442
metric_name: str,
441443
namespace: str,
442444
period: timedelta,
443445
ref_id: str,
444446
metric_id: Optional[str] = None,
445447
stat: str = "Sum",
446448
unit: str = "Count",
449+
fix_metric_name: Optional[str] = None,
447450
**dimensions: str,
448451
) -> "AwsCloudwatchQuery":
449452
dims = "_".join(f"{k}+{v}" for k, v in dimensions.items())
@@ -458,6 +461,7 @@ def create(
458461
metric_id=rid,
459462
stat=stat,
460463
unit=unit,
464+
fix_metric_name=fix_metric_name,
461465
)
462466

463467

@@ -571,7 +575,8 @@ def update_resource_metrics(
571575

572576
for metric_value, maybe_stat_name in normalizer.compute_stats(metric.metric_values):
573577
try:
574-
name = normalizer.metric_name + "_" + normalizer.unit
578+
metric_name = query.fix_metric_name or normalizer.metric_name
579+
name = metric_name + "_" + normalizer.unit
575580
value = normalizer.normalize_value(metric_value)
576581
stat_name = str(maybe_stat_name or normalizer.stat_map[query.stat])
577582

plugins/aws/fix_plugin_aws/resource/ec2.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -572,8 +572,16 @@ def update_atime_mtime() -> None:
572572
elif volume.volume_status == VolumeStatus.AVAILABLE:
573573
vid = volume.id
574574
lookup[vid] = volume
575-
queries.append(AwsCloudwatchQuery.create("VolumeReadOps", "AWS/EBS", delta, vid, VolumeId=vid))
576-
queries.append(AwsCloudwatchQuery.create("VolumeWriteOps", "AWS/EBS", delta, vid, VolumeId=vid))
575+
queries.append(
576+
AwsCloudwatchQuery.create(
577+
metric_name="VolumeReadOps", namespace="AWS/EBS", period=delta, ref_id=vid, VolumeId=vid
578+
)
579+
)
580+
queries.append(
581+
AwsCloudwatchQuery.create(
582+
metric_name="VolumeWriteOps", namespace="AWS/EBS", period=delta, ref_id=vid, VolumeId=vid
583+
)
584+
)
577585

578586
for query, metric in AwsCloudwatchMetricData.query_for(builder, queries, start, now).items():
579587
if non_zero := metric.first_non_zero():

plugins/aws/fix_plugin_aws/resource/rds.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,13 @@ def update_atime_mtime() -> None:
507507
vid = rds.id
508508
lookup[vid] = rds
509509
queries.append(
510-
AwsCloudwatchQuery.create("DatabaseConnections", "AWS/RDS", delta, vid, DBInstanceIdentifier=vid)
510+
AwsCloudwatchQuery.create(
511+
metric_name="DatabaseConnections",
512+
namespace="AWS/RDS",
513+
period=delta,
514+
ref_id=vid,
515+
DBInstanceIdentifier=vid,
516+
)
511517
)
512518

513519
for query, metric in AwsCloudwatchMetricData.query_for(builder, queries, start, now).items():

plugins/aws/fix_plugin_aws/resource/s3.py

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
from collections import defaultdict
23
from json import loads as json_loads
34
from typing import ClassVar, Dict, List, Type, Optional, cast, Any
45

@@ -343,6 +344,14 @@ def _get_tags(self, client: AwsClient) -> Dict[str, str]:
343344

344345
@classmethod
345346
def collect_usage_metrics(cls: Type[AwsResource], builder: GraphBuilder) -> None:
347+
storage_types = {
348+
"StandardStorage": "standard_storage",
349+
"IntelligentTieringStorage": "intelligent_tiering_storage",
350+
"StandardIAStorage": "standard_ia_storage",
351+
"OneZoneIAStorage": "one_zone_ia_storage",
352+
"GlacierStorage": "glacier_storage",
353+
"DeepArchiveStorage": "deep_archive_storage",
354+
}
346355
for region in {
347356
s3_bucket.bucket_location
348357
for s3_bucket in builder.nodes(clazz=AwsS3Bucket)
@@ -367,18 +376,20 @@ def collect_usage_metrics(cls: Type[AwsResource], builder: GraphBuilder) -> None
367376
StorageType="AllStorageTypes",
368377
)
369378
)
370-
queries.append(
371-
AwsCloudwatchQuery.create(
372-
metric_name="BucketSizeBytes",
373-
namespace="AWS/S3",
374-
period=delta,
375-
ref_id=s3_id,
376-
stat="Average",
377-
unit="Bytes",
378-
BucketName=s3.name or s3.safe_name,
379-
StorageType="StandardStorage",
379+
for storage_type, storage_type_name in storage_types.items():
380+
queries.append(
381+
AwsCloudwatchQuery.create(
382+
metric_name="BucketSizeBytes",
383+
namespace="AWS/S3",
384+
period=delta,
385+
ref_id=s3_id,
386+
stat="Average",
387+
unit="Bytes",
388+
fix_metric_name=f"{storage_type_name}_bucket_size",
389+
BucketName=s3.name or s3.safe_name,
390+
StorageType=storage_type,
391+
)
380392
)
381-
)
382393
metric_normalizers = {
383394
"BucketSizeBytes": MetricNormalization(
384395
metric_name=MetricName.BucketSizeBytes,
@@ -395,6 +406,15 @@ def collect_usage_metrics(cls: Type[AwsResource], builder: GraphBuilder) -> None
395406
region_builder = builder.for_region(AwsRegion(id=region, name=region))
396407
cloudwatch_result = AwsCloudwatchMetricData.query_for(region_builder, queries, start, now)
397408
update_resource_metrics(s3s, cloudwatch_result, metric_normalizers)
409+
# Calculate the total bucket size for each bucket by summing up the sizes of all storage types
410+
for s3 in s3s.values():
411+
bucket_size: Dict[str, float] = defaultdict(float)
412+
for metric_name, metric_values in s3._resource_usage.items():
413+
if metric_name.endswith("_bucket_size_bytes"):
414+
for name, value in metric_values.items():
415+
bucket_size[name] += value
416+
if bucket_size:
417+
s3._resource_usage["bucket_size_bytes"] = dict(bucket_size)
398418

399419
def update_resource_tag(self, client: AwsClient, key: str, value: str) -> bool:
400420
tags = self._get_tags(client)

plugins/aws/test/resources/cloudwatch_test.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,20 @@ def test_metrics_filter() -> None:
3939
def test_metric(builder: GraphBuilder) -> None:
4040
now = datetime(2020, 3, 1, tzinfo=timezone.utc)
4141
earlier = now - timedelta(days=60)
42-
read = AwsCloudwatchQuery.create("VolumeReadOps", "AWS/EBS", timedelta(hours=1), "vol-123", VolumeId="vol-123")
43-
write = AwsCloudwatchQuery.create("VolumeWriteOps", "AWS/EBS", timedelta(hours=1), "vol-123", VolumeId="vol-123")
42+
read = AwsCloudwatchQuery.create(
43+
metric_name="VolumeReadOps",
44+
namespace="AWS/EBS",
45+
period=timedelta(hours=1),
46+
ref_id="vol-123",
47+
VolumeId="vol-123",
48+
)
49+
write = AwsCloudwatchQuery.create(
50+
metric_name="VolumeWriteOps",
51+
namespace="AWS/EBS",
52+
period=timedelta(hours=1),
53+
ref_id="vol-123",
54+
VolumeId="vol-123",
55+
)
4456
result = AwsCloudwatchMetricData.query_for(builder, [read, write], earlier, now)
4557
assert result[read].first_non_zero() == (datetime(2020, 1, 18, 17, 40, tzinfo=timezone.utc), 15.0)
4658
assert result[write].first_non_zero() == (datetime(2020, 1, 18, 18, 40, tzinfo=timezone.utc), 12861.0)

0 commit comments

Comments
 (0)