Skip to content

Commit caff155

Browse files
authored
Merge pull request #11 from DigitalPebble/ccf_storage
Storage energy estimator based on CCF
2 parents 86827a2 + d7b0c98 commit caff155

5 files changed

Lines changed: 107 additions & 2 deletions

File tree

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
# SPRUCE
44

5+
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
6+
![Build Status](https://github.com/apache/stormcrawler/actions/workflows/maven.yml/badge.svg)
7+
58
*Spruce* helps estimate the environmental impact of your cloud usage. By leveraging open source models and data, it enriches
69
usage reports generated by cloud providers and allows you to build reports and visualisations. Having the greenops and finops data in the same
710
place makes it easier to expose your costs and impacts side by side.

src/main/java/com/digitalpebble/spruce/SparkJob.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import com.digitalpebble.spruce.modules.ConstantLoad;
66
import com.digitalpebble.spruce.modules.ccf.Networking;
7+
import com.digitalpebble.spruce.modules.ccf.Storage;
78
import com.digitalpebble.spruce.modules.electricitymaps.AverageCarbonIntensity;
89
import com.google.common.collect.ImmutableMap;
910
import org.apache.spark.sql.Dataset;
@@ -50,7 +51,7 @@ public static void main(String[] args) {
5051
// compute emissions
5152

5253
final List<EnrichmentModule> modules = List.of(
53-
new ConstantLoad(),
54+
new Storage(),
5455
new Networking(),
5556
new AverageCarbonIntensity()
5657
);

src/main/java/com/digitalpebble/spruce/modules/ccf/Networking.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* Provides an estimate of energy used for networking in and out of data centres.
1616
* Applies a flat coefficient per Gb
1717
* @see <a href="https://www.cloudcarbonfootprint.org/docs/methodology#networking">CCF methodology</a>
18-
* @see <a href="https://github.com/cloud-carbon-footprint/cloud-carbon-footprint/blob/main/packages/aws/src/lib/CostAndUsageTypes.ts#L108">resource file</a>
18+
* @see <a href="https://github.com/cloud-carbon-footprint/cloud-carbon-footprint/blob/9f2cf436e5ad020830977e52c3b0a1719d20a8b9/packages/aws/src/lib/CostAndUsageTypes.ts#L25">resource file</a>
1919
**/
2020
public class Networking implements EnrichmentModule {
2121

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
package com.digitalpebble.spruce.modules.ccf;
4+
5+
import com.digitalpebble.spruce.Column;
6+
import com.digitalpebble.spruce.EnrichmentModule;
7+
import org.apache.spark.sql.Row;
8+
9+
import java.util.Map;
10+
11+
import static com.digitalpebble.spruce.CURColumn.*;
12+
import static com.digitalpebble.spruce.CarbonaraColumn.ENERGY_USED;
13+
14+
/**
15+
* Provides an estimate of energy used for storage.
16+
* Applies a flat coefficient per Gb
17+
* @see <a href="https://www.cloudcarbonfootprint.org/docs/methodology#storage">CCF methodology</a>
18+
* @see <a href="https://github.com/cloud-carbon-footprint/cloud-carbon-footprint/blob/9f2cf436e5ad020830977e52c3b0a1719d20a8b9/packages/aws/src/lib/CostAndUsageTypes.ts#L25">resource file</a>
19+
**/
20+
public class Storage implements EnrichmentModule {
21+
22+
// 0.65 Watt-Hours per Terabyte-Hour for HDD
23+
final double hdd_gb_coefficient = 0.65 / 1024d;
24+
// 1.2 Watt-Hours per Terabyte-Hour for SSD
25+
final double ssd_gb_coefficient = 1.2 / 1024d;
26+
27+
@Override
28+
public Column[] columnsAdded() {
29+
return new Column[]{ENERGY_USED};
30+
}
31+
32+
@Override
33+
public Row process(Row row) {
34+
String operation = LINE_ITEM_OPERATION.getString(row);
35+
if (operation == null || !operation.startsWith("CreateVolume")) {
36+
return row;
37+
}
38+
39+
// in gb months
40+
double amount_gb = USAGE_AMOUNT.getDouble(row);
41+
42+
// work out which coefficient should be applied
43+
// if the line item operation is CreateVolume without a suffix then it is hdd, sdd otherwise
44+
// (https://docs.aws.amazon.com/ebs/latest/userguide/ebs-volume-types.html)
45+
boolean isHDD = operation.equals("CreateVolume");
46+
47+
double coefficient = isHDD? hdd_gb_coefficient : ssd_gb_coefficient;
48+
49+
double energy_gb = amount_gb * coefficient;
50+
51+
return EnrichmentModule.withUpdatedValue(row, ENERGY_USED, energy_gb);
52+
}
53+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
package com.digitalpebble.spruce.modules.ccf;
4+
5+
import org.apache.spark.sql.Row;
6+
import org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema;
7+
import org.apache.spark.sql.types.StructType;
8+
import org.junit.jupiter.api.Test;
9+
10+
import static org.junit.jupiter.api.Assertions.*;
11+
12+
class StorageTest {
13+
14+
private final Storage storage = new Storage();
15+
16+
@Test
17+
void processCreateVolumeSSD() {
18+
String ddl = "line_item_operation STRING, line_item_usage_amount DOUBLE, energy_usage_kwh DOUBLE";
19+
StructType schema = StructType.fromDDL(ddl);
20+
Object[] values = new Object[] {"CreateVolume-Gp3", 10d, null};
21+
Row row = new GenericRowWithSchema(values, schema);
22+
Row result = storage.process(row);
23+
double expected = 10d * storage.ssd_gb_coefficient;
24+
assertEquals(expected, result.getDouble(2));
25+
}
26+
27+
@Test
28+
void processCreateVolumeSSD2() {
29+
String ddl = "line_item_operation STRING, line_item_usage_amount DOUBLE, energy_usage_kwh DOUBLE";
30+
StructType schema = StructType.fromDDL(ddl);
31+
Object[] values = new Object[] {"CreateVolume-Gp2", 10d, null};
32+
Row row = new GenericRowWithSchema(values, schema);
33+
Row result = storage.process(row);
34+
double expected = 10d * storage.ssd_gb_coefficient;
35+
assertEquals(expected, result.getDouble(2));
36+
}
37+
38+
@Test
39+
void processCreateVolumeHDD() {
40+
String ddl = "line_item_operation STRING, line_item_usage_amount DOUBLE, energy_usage_kwh DOUBLE";
41+
StructType schema = StructType.fromDDL(ddl);
42+
Object[] values = new Object[] {"CreateVolume", 10d, null};
43+
Row row = new GenericRowWithSchema(values, schema);
44+
Row result = storage.process(row);
45+
double expected = 10d * storage.hdd_gb_coefficient;
46+
assertEquals(expected, result.getDouble(2));
47+
}
48+
}

0 commit comments

Comments
 (0)