summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java3
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/CostInfo.java69
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/MeteringClient.java6
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/MockTenantCost.java37
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/TenantCost.java34
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockMeteringClient.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java51
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java6
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java46
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/cost-report.json20
10 files changed, 18 insertions, 258 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java
index dec5d9264a7..f545cb8ff47 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/ServiceRegistry.java
@@ -17,7 +17,6 @@ import com.yahoo.vespa.hosted.controller.api.integration.organization.Mailer;
import com.yahoo.vespa.hosted.controller.api.integration.organization.OwnershipIssues;
import com.yahoo.vespa.hosted.controller.api.integration.resource.CostReportConsumer;
import com.yahoo.vespa.hosted.controller.api.integration.resource.MeteringClient;
-import com.yahoo.vespa.hosted.controller.api.integration.resource.TenantCost;
import com.yahoo.vespa.hosted.controller.api.integration.routing.GlobalRoutingService;
import com.yahoo.vespa.hosted.controller.api.integration.routing.RoutingGenerator;
@@ -71,6 +70,4 @@ public interface ServiceRegistry {
// TODO: No longer used. Remove this once untangled from test code
BuildService buildService();
- TenantCost tenantCost();
-
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/CostInfo.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/CostInfo.java
deleted file mode 100644
index 50c257acd23..00000000000
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/CostInfo.java
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.api.integration.resource;
-
-import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.zone.ZoneId;
-
-import java.math.BigDecimal;
-
-/**
- * @author olaa
- */
-public class CostInfo {
-
- private final ApplicationId applicationId;
- private final ZoneId zoneId;
- private final BigDecimal cpuHours;
- private final BigDecimal memoryHours;
- private final BigDecimal diskHours;
- private final int cpuCost;
- private final int memoryCost;
- private final int diskCost;
-
-
- public CostInfo(ApplicationId applicationId, ZoneId zoneId,
- BigDecimal cpuHours, BigDecimal memoryHours, BigDecimal diskHours,
- int cpuCost, int memoryCost, int diskCost) {
- this.applicationId = applicationId;
- this.zoneId = zoneId;
- this.cpuHours = cpuHours;
- this.memoryHours = memoryHours;
- this.diskHours = diskHours;
- this.cpuCost = cpuCost;
- this.memoryCost = memoryCost;
- this.diskCost = diskCost;
- }
-
- public ApplicationId getApplicationId() {
- return applicationId;
- }
-
- public ZoneId getZoneId() {
- return zoneId;
- }
-
- public BigDecimal getCpuHours() {
- return cpuHours;
- }
-
- public BigDecimal getMemoryHours() {
- return memoryHours;
- }
-
- public BigDecimal getDiskHours() {
- return diskHours;
- }
-
- public int getCpuCost() {
- return cpuCost;
- }
-
- public int getMemoryCost() {
- return memoryCost;
- }
-
- public int getDiskCost() {
- return diskCost;
- }
-
-}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/MeteringClient.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/MeteringClient.java
index 3e06b24c6be..632dbaad419 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/MeteringClient.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/MeteringClient.java
@@ -1,10 +1,8 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.api.integration.resource;
-import com.yahoo.config.provision.ApplicationName;
-import com.yahoo.config.provision.TenantName;
-
import java.util.Collection;
+import java.util.List;
/**
* Consumes and retrieves snapshots of resources allocated per application.
@@ -15,6 +13,6 @@ public interface MeteringClient {
void consume(Collection<ResourceSnapshot> resources);
- MeteringInfo getResourceSnapshots(TenantName tenantName, ApplicationName applicationName);
+ MeteringInfo getResourceSnapshots(String tenantName, String applicationName);
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/MockTenantCost.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/MockTenantCost.java
deleted file mode 100644
index 03ba44e04c7..00000000000
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/MockTenantCost.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.api.integration.resource;
-
-import com.yahoo.config.provision.TenantName;
-
-import java.time.LocalDate;
-import java.time.YearMonth;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-/**
- * @author olaa
- */
-public class MockTenantCost implements TenantCost {
-
- private Set<YearMonth> monthsOfMetering = Collections.emptySet();
- private List<CostInfo> costInfoList = Collections.emptyList();
-
- @Override
- public Set<YearMonth> monthsWithMetering(TenantName tenantName) {
- return monthsOfMetering;
- }
-
- @Override
- public List<CostInfo> getTenantCostOfMonth(TenantName tenantName, YearMonth month) {
- return costInfoList;
- }
-
- public void setMonthsWithMetering(Set<YearMonth> monthsOfMetering) {
- this.monthsOfMetering = monthsOfMetering;
- }
-
- public void setCostInfoList(List<CostInfo> costInfoList) {
- this.costInfoList = costInfoList;
- }
-}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/TenantCost.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/TenantCost.java
deleted file mode 100644
index b4ca0cd7076..00000000000
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/TenantCost.java
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.api.integration.resource;
-
-import com.yahoo.config.provision.TenantName;
-
-import java.time.LocalDate;
-import java.time.YearMonth;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-/**
- * @author olaa
- */
-public interface TenantCost {
-
- Set<YearMonth> monthsWithMetering(TenantName tenantName);
-
- List<CostInfo> getTenantCostOfMonth(TenantName tenantName, YearMonth month);
-
- static TenantCost empty() {
- return new TenantCost() {
- @Override
- public Set<YearMonth> monthsWithMetering(TenantName tenantName) {
- return Collections.emptySet();
- }
-
- @Override
- public List<CostInfo> getTenantCostOfMonth(TenantName tenantName, YearMonth month) {
- return Collections.emptyList();
- }
- };
- }
-}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockMeteringClient.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockMeteringClient.java
index 45ead36f622..10e1eb39c8a 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockMeteringClient.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockMeteringClient.java
@@ -1,8 +1,6 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.api.integration.stubs;
-import com.yahoo.config.provision.ApplicationName;
-import com.yahoo.config.provision.TenantName;
import com.yahoo.vespa.hosted.controller.api.integration.resource.MeteringInfo;
import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceAllocation;
import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshot;
@@ -28,7 +26,7 @@ public class MockMeteringClient implements MeteringClient {
}
@Override
- public MeteringInfo getResourceSnapshots(TenantName tenantName, ApplicationName applicationName) {
+ public MeteringInfo getResourceSnapshots(String tenantName, String applicationName) {
return meteringInfo.orElseGet(() -> {
ResourceAllocation emptyAllocation = new ResourceAllocation(0, 0, 0);
return new MeteringInfo(emptyAllocation, emptyAllocation, emptyAllocation, Collections.emptyMap());
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
index 29806f1355c..5d83f4848e5 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
@@ -8,7 +8,6 @@ import com.google.inject.Inject;
import com.yahoo.component.Version;
import com.yahoo.config.application.api.DeploymentSpec;
import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.ApplicationName;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.TenantName;
@@ -53,7 +52,6 @@ import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationV
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.SourceRevision;
-import com.yahoo.vespa.hosted.controller.api.integration.resource.CostInfo;
import com.yahoo.vespa.hosted.controller.api.integration.resource.MeteringInfo;
import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceAllocation;
import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshot;
@@ -94,7 +92,6 @@ import javax.ws.rs.NotAuthorizedException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.math.RoundingMode;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.DigestInputStream;
@@ -103,8 +100,7 @@ import java.security.PublicKey;
import java.time.DayOfWeek;
import java.time.Duration;
import java.time.Instant;
-import java.time.YearMonth;
-import java.time.format.DateTimeParseException;
+import java.time.LocalDate;
import java.util.Arrays;
import java.util.Base64;
import java.util.Comparator;
@@ -116,6 +112,7 @@ import java.util.Scanner;
import java.util.Set;
import java.util.StringJoiner;
import java.util.logging.Level;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static com.yahoo.jdisc.Response.Status.BAD_REQUEST;
@@ -370,13 +367,10 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
}
private HttpResponse tenantCost(Tenant tenant, HttpRequest request) {
- Set<YearMonth> months = controller.serviceRegistry().tenantCost().monthsWithMetering(tenant.name());
-
var slime = new Slime();
var objectCursor = slime.setObject();
var monthsCursor = objectCursor.setArray("months");
- months.forEach(month -> monthsCursor.addString(month.toString()));
return new SlimeJsonResponse(slime);
}
@@ -386,37 +380,22 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
.orElseGet(() -> ErrorResponse.notFoundError("Tenant '" + tenantName + "' does not exist"));
}
- private YearMonth tenantCostParseDate(String dateString) {
- try {
- return YearMonth.parse(dateString);
- } catch (DateTimeParseException e){
+ private LocalDate tenantCostParseDate(String dateString) {
+ var monthPattern = Pattern.compile("^(?<year>[0-9]{4})-(?<month>[0-9]{2})$");
+ var matcher = monthPattern.matcher(dateString);
+
+ if (matcher.matches()) {
+ var year = Integer.parseInt(matcher.group("year"));
+ var month = Integer.parseInt(matcher.group("month"));
+ return LocalDate.of(year, month, 1);
+ } else {
throw new IllegalArgumentException("Could not parse year-month '" + dateString + "'");
}
}
- private HttpResponse tenantCost(Tenant tenant, YearMonth month, HttpRequest request) {
+ private HttpResponse tenantCost(Tenant tenant, LocalDate month, HttpRequest request) {
var slime = new Slime();
- Cursor cursor = slime.setObject();
- cursor.setString("month", month.toString());
- List<CostInfo> costInfos = controller.serviceRegistry().tenantCost()
- .getTenantCostOfMonth(tenant.name(), month);
- Cursor array = cursor.setArray("items");
-
- costInfos.forEach(costInfo -> {
- Cursor costObject = array.addObject();
- costObject.setString("applicationId", costInfo.getApplicationId().serializedForm());
- costObject.setString("zoneId", costInfo.getZoneId().value());
- Cursor cpu = costObject.setObject("cpu");
- cpu.setDouble("usage", costInfo.getCpuHours().setScale(1, RoundingMode.HALF_UP).doubleValue());
- cpu.setLong("charge", costInfo.getCpuCost());
- Cursor memory = costObject.setObject("memory");
- memory.setDouble("usage", costInfo.getMemoryHours().setScale(1, RoundingMode.HALF_UP).doubleValue());
- memory.setLong("charge", costInfo.getMemoryCost());
- Cursor disk = costObject.setObject("disk");
- disk.setDouble("usage", costInfo.getDiskHours().setScale(1, RoundingMode.HALF_UP).doubleValue());
- disk.setLong("charge", costInfo.getDiskCost());
- });
-
+ slime.setObject();
return new SlimeJsonResponse(slime);
}
@@ -1149,9 +1128,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
Slime slime = new Slime();
Cursor root = slime.setObject();
- MeteringInfo meteringInfo = controller.serviceRegistry()
- .meteringService()
- .getResourceSnapshots(TenantName.from(tenant), ApplicationName.from(application));
+ MeteringInfo meteringInfo = controller.serviceRegistry().meteringService().getResourceSnapshots(tenant, application);
ResourceAllocation currentSnapshot = meteringInfo.getCurrentSnapshot();
Cursor currentRate = root.setObject("currentrate");
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
index 416f2ee89ad..965e1db0e2e 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
@@ -28,8 +28,6 @@ import com.yahoo.vespa.hosted.controller.api.integration.organization.MockIssueH
import com.yahoo.vespa.hosted.controller.api.integration.organization.OwnershipIssues;
import com.yahoo.vespa.hosted.controller.api.integration.resource.CostReportConsumer;
import com.yahoo.vespa.hosted.controller.api.integration.resource.MeteringClient;
-import com.yahoo.vespa.hosted.controller.api.integration.resource.MockTenantCost;
-import com.yahoo.vespa.hosted.controller.api.integration.resource.TenantCost;
import com.yahoo.vespa.hosted.controller.api.integration.routing.GlobalRoutingService;
import com.yahoo.vespa.hosted.controller.api.integration.routing.MemoryGlobalRoutingService;
import com.yahoo.vespa.hosted.controller.api.integration.routing.RoutingGenerator;
@@ -71,7 +69,6 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg
private final ApplicationStoreMock applicationStoreMock = new ApplicationStoreMock();
private final MockRunDataStore mockRunDataStore = new MockRunDataStore();
private final MockBuildService mockBuildService = new MockBuildService();
- private final MockTenantCost mockTenantCost = new MockTenantCost();
@Override
public ConfigServer configServer() {
@@ -173,9 +170,6 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg
return memoryNameService;
}
- @Override
- public TenantCost tenantCost() { return mockTenantCost;}
-
public ZoneRegistryMock zoneRegistryMock() {
return zoneRegistryMock;
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
index 36366a34fa5..09fd804ea0c 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
@@ -40,9 +40,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.api.integration.organization.Contact;
import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId;
import com.yahoo.vespa.hosted.controller.api.integration.organization.User;
-import com.yahoo.vespa.hosted.controller.api.integration.resource.CostInfo;
import com.yahoo.vespa.hosted.controller.api.integration.resource.MeteringInfo;
-import com.yahoo.vespa.hosted.controller.api.integration.resource.MockTenantCost;
import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceAllocation;
import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshot;
import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockMeteringClient;
@@ -77,13 +75,10 @@ import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
-import java.math.BigDecimal;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
-import java.time.LocalDate;
-import java.time.YearMonth;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
@@ -92,7 +87,6 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
-import java.util.TreeSet;
import java.util.function.Supplier;
import static com.yahoo.application.container.handler.Request.Method.DELETE;
@@ -206,7 +200,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
// GET cost for a month for a tenant
tester.assertResponse(request("/application/v4/tenant/tenant1/cost/2018-01", GET).userIdentity(USER_ID).oktaAccessToken(OKTA_AT),
- "{\"month\":\"2018-01\",\"items\":[]}");
+ "{}");
// Add another Athens domain, so we can try to create more tenants
createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN_2, USER_ID); // New domain to test tenant w/property ID
@@ -1078,44 +1072,6 @@ public class ApplicationApiTest extends ControllerContainerTest {
}
@Test
- public void testTenantCostResponse() {
- ApplicationId applicationId = createTenantAndApplication();
- MockTenantCost mockTenantCost = (MockTenantCost) controllerTester.containerTester().serviceRegistry().tenantCost();
-
- mockTenantCost.setMonthsWithMetering(
- new TreeSet<>(Set.of(
- YearMonth.of(2019, 10),
- YearMonth.of(2019, 9)
- ))
- );
-
- tester.assertResponse(request("/application/v4/tenant/" + applicationId.tenant().value() + "/cost", GET)
- .userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT),
- "{\"months\":[\"2019-09\",\"2019-10\"]}");
-
- CostInfo costInfo1 = new CostInfo(applicationId, ZoneId.from("prod", "us-south-1"),
- new BigDecimal("7.0"),
- new BigDecimal("600.0"),
- new BigDecimal("1000.0"),
- 35, 23, 10);
- CostInfo costInfo2 = new CostInfo(applicationId, ZoneId.from("prod", "us-north-1"),
- new BigDecimal("2.0"),
- new BigDecimal("3.0"),
- new BigDecimal("4.0"),
- 10, 20, 30);
-
- mockTenantCost.setCostInfoList(
- List.of(costInfo1, costInfo2)
- );
-
- tester.assertResponse(request("/application/v4/tenant/" + applicationId.tenant().value() + "/cost/2019-09", GET)
- .userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT),
- new File("cost-report.json"));
- }
-
- @Test
public void testErrorResponses() throws Exception {
tester.computeVersionStatus();
createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN, USER_ID);
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/cost-report.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/cost-report.json
deleted file mode 100644
index 8f6dbf17d51..00000000000
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/cost-report.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "month": "2019-09",
- "items": [
- {
- "applicationId":"tenant1:application1:instance1",
- "zoneId":"prod.us-south-1",
- "cpu": {"usage":7.0,"charge":35},
- "memory": {"usage":600.0,"charge":23},
- "disk": {"usage":1000.0,"charge":10}
- },
- {
- "applicationId":"tenant1:application1:instance1",
- "zoneId":"prod.us-north-1",
- "cpu": {"usage":2.0,"charge":10},
- "memory": {"usage":3.0,"charge":20},
- "disk": {"usage":4.0,"charge":30}
- }
- ]
-
-} \ No newline at end of file