com.vmware.admiral.compute.container.maintenance.ContainerStatsEvaluator.java Source code

Java tutorial

Introduction

Here is the source code for com.vmware.admiral.compute.container.maintenance.ContainerStatsEvaluator.java

Source

/*
 * Copyright (c) 2016 VMware, Inc. All Rights Reserved.
 *
 * This product is licensed to you under the Apache License, Version 2.0 (the "License").
 * You may not use this product except in compliance with the License.
 *
 * This product may include a number of subcomponents with separate copyright notices
 * and license terms. Your use of these subcomponents is subject to the terms and
 * conditions of the subcomponent's license, as noted in the LICENSE file.
 */

package com.vmware.admiral.compute.container.maintenance;

import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;

import com.vmware.xenon.common.Utils;

/**
 * Calculate the container stats based on the json data coming from the Docker hosts.
 */
public class ContainerStatsEvaluator {

    private static final String CONTAINER_STOPPED_TIME = "0001-01-01T00:00:00Z";

    /**
     * Parse the json stats value and return ContainerStats state with the calculated values from
     * the json field.
     *
     * @return ContainerStats with parsed and calculated stats value.
     */
    public static ContainerStats calculateStatsValues(String statsJson) {
        ContainerStats state = new ContainerStats();
        if (statsJson == null || statsJson.isEmpty()) {
            return state;
        }

        Map<String, JsonElement> stats = parseStats(statsJson);

        calculateCpuUsage(state, stats);

        setMemoryUsage(state, stats);

        setNetworkUsage(state, stats);

        setContainerStopped(state, stats);

        return state;
    }

    private static Map<String, JsonElement> parseStats(String statsJson) {
        Map<String, JsonElement> stats = null;
        try {
            stats = Utils.fromJson(statsJson, new TypeToken<Map<String, JsonElement>>() {
            }.getType());
        } catch (Exception e) {
            Utils.logWarning("Error parsing container stats: [%s]. Error: %s", statsJson, Utils.toString(e));
        }
        return stats;
    }

    private static void setNetworkUsage(ContainerStats state, Map<String, JsonElement> stats) {
        try {
            JsonElement jsonElement = stats.get("networks");
            if (jsonElement == null) {
                return;
            }
            JsonObject networks = jsonElement.getAsJsonObject();
            if (networks == null) {
                return;
            }
            Set<Entry<String, JsonElement>> entrySet = networks.entrySet();
            if (entrySet == null) {
                return;
            }

            NetworkTraffic summedTraffic = entrySet.stream().map(entry -> {

                JsonObject network = entry.getValue().getAsJsonObject();
                NetworkTraffic result = new NetworkTraffic();

                if (network != null) {
                    JsonElement netInValue = network.get("rx_bytes");
                    if (netInValue != null) {
                        result.networkIn = netInValue.getAsLong();
                    }

                    JsonElement netOutValue = network.get("tx_bytes");
                    if (netOutValue != null) {
                        result.networkOut += netOutValue.getAsLong();
                    }
                }

                return result;

            }).reduce(new NetworkTraffic(), (t1, t2) -> {

                return new NetworkTraffic(t1.networkIn + t2.networkIn, t1.networkOut + t2.networkOut);
            });

            state.networkIn = summedTraffic.networkIn;
            state.networkOut = summedTraffic.networkOut;

        } catch (Exception e) {
            Utils.logWarning("Error during container stats network usage parsing: %s", Utils.toString(e));
        }
    }

    private static void setMemoryUsage(ContainerStats state, Map<String, JsonElement> stats) {
        try {
            JsonElement jsonElement = stats.get("memory_stats");
            if (jsonElement == null) {
                return;
            }
            JsonObject memory_stats = jsonElement.getAsJsonObject();
            JsonElement limitValue = memory_stats.get("limit");
            if (limitValue != null) {
                state.memLimit = limitValue.getAsLong();
            }
            JsonElement usage = memory_stats.get("usage");
            if (usage != null) {
                state.memUsage = usage.getAsLong();
            }
        } catch (Exception e) {
            Utils.logWarning("Error during container stats memory usage parsing: %s", Utils.toString(e));
        }
    }

    // Calculate Docker container CPU percentage usage as implemented by the command line tool -
    // https://github.com/docker/docker/blob/master/api/client/stats.go#L195
    private static void calculateCpuUsage(ContainerStats state, Map<String, JsonElement> stats) {
        try {
            JsonElement cpu_stats_json = stats.get("cpu_stats");
            if (cpu_stats_json == null || cpu_stats_json.isJsonNull()) {
                Utils.logWarning("cpu_stats is null.");
                return;
            }
            JsonObject cpu_stats = cpu_stats_json.getAsJsonObject();

            JsonElement systemCpuUsageValue = cpu_stats.get("system_cpu_usage");
            if (systemCpuUsageValue == null || systemCpuUsageValue.isJsonNull()) {
                Utils.logWarning("system_cpu_usage is null.");
                return;
            }
            long system_cpu_usage = systemCpuUsageValue.getAsLong();

            JsonElement cpu_usage_json = cpu_stats.get("cpu_usage");
            if (cpu_usage_json == null || cpu_usage_json.isJsonNull()) {
                Utils.logWarning("cpu_usage is null.");
                return;
            }
            JsonObject cpu_usage = cpu_usage_json.getAsJsonObject();

            JsonElement totalUsageValue = cpu_usage.get("total_usage");
            if (totalUsageValue == null || totalUsageValue.isJsonNull()) {
                Utils.logWarning("totalUsageValue is null.");
                return;
            }
            long total_usage = totalUsageValue.getAsLong();

            JsonElement percpu_usage_json = cpu_usage.get("percpu_usage");
            if (percpu_usage_json == null || percpu_usage_json.isJsonNull()) {
                Utils.logWarning("percpu_usage is null.");
                return;
            }
            JsonArray percpu_usage = percpu_usage_json.getAsJsonArray();

            JsonElement precpu_stats_json = stats.get("precpu_stats");
            if (precpu_stats_json == null || precpu_stats_json.isJsonNull()) {
                Utils.logWarning("precpu_stats is null.");
                return;
            }
            JsonObject precpu_stats = precpu_stats_json.getAsJsonObject();

            JsonElement system_cpu_usage_json = precpu_stats.get("system_cpu_usage");
            if (system_cpu_usage_json == null || system_cpu_usage_json.isJsonNull()) {
                Utils.logWarning("system_cpu_usage is null.");
                return;
            }
            long presystem_cpu_usage = system_cpu_usage_json.getAsLong();

            JsonElement precpu_usage_json = precpu_stats.get("cpu_usage");
            if (precpu_usage_json == null || precpu_usage_json.isJsonNull()) {
                Utils.logWarning("precpu_usage is null.");
                return;
            }
            JsonObject precpu_usage = precpu_usage_json.getAsJsonObject();

            JsonElement pretotal_usage_json = precpu_usage.get("total_usage");
            if (pretotal_usage_json == null || pretotal_usage_json.isJsonNull()) {
                Utils.logWarning("total_usage is null.");
                return;
            }

            long pretotal_usage = pretotal_usage_json.getAsLong();

            long cpuDelta = total_usage - pretotal_usage;
            long systemDelta = system_cpu_usage - presystem_cpu_usage;

            if (systemDelta > 0 && cpuDelta > 0) {
                double cpuUsage = (((double) cpuDelta / systemDelta) * percpu_usage.size()) * 100.0;
                state.cpuUsage = Math.round(cpuUsage * 100d) / 100d;
            }

        } catch (Exception e) {
            Utils.logWarning("Error during container stats CPU usage calculations: %s", Utils.toString(e));
        }
    }

    private static void setContainerStopped(ContainerStats state, Map<String, JsonElement> stats) {
        try {
            JsonElement read_json = stats.get("read");
            if (read_json == null || read_json.isJsonNull()) {
                Utils.logWarning("read is null.");
                return;
            }
            String read = read_json.getAsString();
            state.containerStopped = Boolean.FALSE;
            if (CONTAINER_STOPPED_TIME.equals(read)) {
                state.containerStopped = Boolean.TRUE;
            }
        } catch (Exception e) {
            Utils.logWarning("Error during container stats status calculations: %s", Utils.toString(e));
        }
    }

    private static class NetworkTraffic {

        public long networkIn;
        public long networkOut;

        public NetworkTraffic() {
            this(0, 0);
        }

        public NetworkTraffic(long networkIn, long networkOut) {
            this.networkIn = networkIn;
            this.networkOut = networkOut;
        }

    }
}