Skip to content

Commit

Permalink
After adding a VCH, CPU and Memory are not reported for the host
Browse files Browse the repository at this point in the history
Used memory, total cpu and used cpu are reported by VCH in a different way
than Docker: they are reported as part of docker info REST call, i.e.:
{
    "Architecture": "x86_64",
    ...
    "SystemStatus": [
        ...
        [
            " VCH CPU limit",
            "7715 MHz"
        ],
        [
            " VCH memory limit",
            "6.422 GiB"
        ],
        [
            " VCH CPU usage",
            "145 MHz"
        ],
        [
            " VCH memory usage",
            "3.109 GiB"
        ],
        ...
    ],
    ...
}

This change parses SystemStatus contents to extract the relevant fields from
the list of results, compute the desired values (available mem = total mem -
used mem, cpu usage % = 100 * cpu usage / cpu limit) and put them as custom
properties for computeState. If the input doesn't match the expected format,
the custom properties are not set, but the method does not throw exception.

Add also a simple unit util convert for memory and cpu usage, because VCH
reports the results in format "magnitude unit".

Add a unit test for docker info for VCH, and some refactor on
com.vmware.admiral.adapter.docker.service package (several methods are repeated
there).

Change-Id: I9dd43c5e7d100afade2267d728b2a4b175429f74
Reviewed-on: http://bellevue-ci.eng.vmware.com:8080/8012
Reviewed-by: Sergio Sanchez <[email protected]>
Compute-Verified: jenkins <[email protected]>
Upgrade-Verified: jenkins <[email protected]>
Bellevue-Verified: jenkins <[email protected]>
CS-Verified: jenkins <[email protected]>
  • Loading branch information
jdillet committed Mar 20, 2017
1 parent a92dcc5 commit 547ebd6
Show file tree
Hide file tree
Showing 10 changed files with 623 additions and 162 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@

import com.vmware.admiral.adapter.common.ContainerHostOperationType;
import com.vmware.admiral.common.ManagementUriParts;
import com.vmware.admiral.common.util.ConversionUtil;
import com.vmware.admiral.common.util.PropertyUtils;
import com.vmware.admiral.compute.ContainerHostService;
import com.vmware.admiral.compute.ContainerHostUtil;
import com.vmware.admiral.compute.container.ContainerDescriptionService.ContainerDescription;
Expand Down Expand Up @@ -63,6 +65,12 @@ public class DockerHostAdapterService extends AbstractDockerAdapterService {
private static final String COMMAND_CPU_USAGE = "awk -v a=\"$(awk '/cpu /{print $2+$4,$2+$4+$5}' /proc/stat; sleep 1)\" '/cpu /{split(a,b,\" \"); print 100*($2+$4-b[1])/($2+$4+$5-b[2])}' /proc/stat";
private static final String HIDDEN_CUSTOM_PROPERTY_PREFIX = "__";

// constats to extract VCH usage data
private static final String SYSTEM_STATUS = "SystemStatus";
private static final String VCH_MEMORY_USAGE = " VCH memory usage";
private static final String VCH_CPU_LIMIT = " VCH CPU limit";
private static final String VCH_CPU_USAGE = " VCH CPU usage";

@Override
public void handlePatch(Operation op) {
ContainerHostRequest request = op.getBody(ContainerHostRequest.class);
Expand Down Expand Up @@ -494,6 +502,10 @@ private void updateHostStateCustomProperties(ComputeState computeState,

computeState.customProperties.remove(
ContainerHostService.NUMBER_OF_CONTAINERS_PER_HOST_PROP_NAME);

if (ContainerHostUtil.isVicHost(computeState)) {
parseVicStats(computeState, properties);
}
}
}

Expand Down Expand Up @@ -600,4 +612,69 @@ private void updateSslTrust(ContainerHostRequest request, CommandInput commandIn
request.customProperties.get(SSL_TRUST_ALIAS_PROP_NAME));
}

private void parseVicStats(ComputeState computeState, Map<String, Object> properties) {
Long totalMemory = PropertyUtils.getPropertyLong(computeState.customProperties,
ContainerHostService.DOCKER_HOST_TOTAL_MEMORY_PROP_NAME).orElse(0L);
Double usedMemory = Double.valueOf(0);
Long totalCpu = Long.valueOf(0);
Long usedCpu = Long.valueOf(0);
try {
@SuppressWarnings("unchecked")
List<List<String>> systemStatus = (List<List<String>>) properties.get(SYSTEM_STATUS);
// parse SystemStatus in the best possible way, but do not fail if result is not in
// expected format
for (List<String> status : systemStatus) {
if (status == null || status.size() < 2) {
continue;
}
if (VCH_MEMORY_USAGE.equals(status.get(0))) {
usedMemory = getMemoryStatus(status);
} else if (VCH_CPU_LIMIT.equals(status.get(0))) {
totalCpu = getCpuStatus(status);
} else if (VCH_CPU_USAGE.equals(status.get(0))) {
usedCpu = getCpuStatus(status);
}
}
String cpuUsagePct = "0";
if (totalCpu != 0) {
cpuUsagePct = Double.toString(100.0 * usedCpu / totalCpu);
}
computeState.customProperties.put(
ContainerHostService.DOCKER_HOST_AVAILABLE_MEMORY_PROP_NAME,
Double.toString(totalMemory - usedMemory));
if (totalCpu != 0) {
computeState.customProperties.put(
ContainerHostService.DOCKER_HOST_CPU_USAGE_PCT_PROP_NAME,
cpuUsagePct);
}
} catch (Exception e) {
logWarning("Unable to parse SystemStatus contents: %s", e.getMessage());
}
}

private Double getMemoryStatus(List<String> status) {
if (status.get(0) == null || status.get(1) == null) {
logWarning("Unable to parse memory status for VIC host");
return Double.valueOf(0);
}
String[] sp = status.get(1).split(" ");
if (sp.length < 2) {
logWarning("Unable to parse memory status for VIC host");
return Double.valueOf(0);
}
return ConversionUtil.memoryToBytes(Double.parseDouble(sp[0]), sp[1]);
}

private Long getCpuStatus(List<String> status) {
if (status.get(0) == null || status.get(1) == null) {
logWarning("Unable to parse CPU status for VIC host");
return Long.valueOf(0);
}
String[] sp = status.get(1).split(" ");
if (sp.length < 2) {
logWarning("Unable to parse CPU status for VIC host");
return Long.valueOf(0);
}
return ConversionUtil.cpuToHertz(Long.parseLong(sp[0]), sp[1]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,27 @@
import org.junit.After;
import org.junit.Before;

import com.vmware.admiral.adapter.common.ContainerHostOperationType;
import com.vmware.admiral.adapter.common.service.mock.MockTaskFactoryService;
import com.vmware.admiral.adapter.common.service.mock.MockTaskService.MockTaskState;
import com.vmware.admiral.adapter.docker.service.ContainerHostRequest;
import com.vmware.admiral.common.test.BaseTestCase;
import com.vmware.admiral.common.test.HostInitTestDcpServicesConfig;
import com.vmware.admiral.host.ComputeInitialBootService;
import com.vmware.admiral.host.HostInitCommonServiceConfig;
import com.vmware.admiral.host.HostInitComputeServicesConfig;
import com.vmware.admiral.host.HostInitPhotonModelServiceConfig;
import com.vmware.admiral.service.common.ServiceTaskCallback;
import com.vmware.admiral.service.common.SslTrustCertificateService;
import com.vmware.admiral.service.common.SslTrustCertificateService.SslTrustCertificateState;
import com.vmware.admiral.service.common.TaskServiceDocument;
import com.vmware.photon.controller.model.resources.ComputeService.ComputeState;
import com.vmware.xenon.common.Operation;
import com.vmware.xenon.common.ServiceHost;
import com.vmware.xenon.common.TaskState;
import com.vmware.xenon.common.UriUtils;
import com.vmware.xenon.common.test.VerificationHost;
import com.vmware.xenon.services.common.AuthCredentialsService;
import com.vmware.xenon.services.common.AuthCredentialsService.AuthCredentialsServiceState;

/**
Expand All @@ -45,8 +54,13 @@ public class BaseMockDockerTestCase extends BaseTestCase {
private static final String DOCKER_CLIENT_CERTIFICATE = "test.docker.client.certificate";
private static final String DOCKER_SERVER_CERTIFICATE = "test.docker.server.certificate";
protected static final long FUTURE_TIMEOUT_SECONDS = 10;
protected static final String TASK_INFO_STAGE = TaskServiceDocument.FIELD_NAME_TASK_STAGE;

protected VerificationHost mockDockerHost;
protected URI dockerHostAdapterServiceUri;
protected ComputeState dockerHostState;
protected String testDockerCredentialsLink;
protected String provisioningTaskLink;

protected static URI dockerUri;
protected static URI dockerVersionedUri;
Expand Down Expand Up @@ -186,6 +200,91 @@ protected boolean isMockTarget() {
return mockDockerHost != null;
}

protected ComputeState requestDockerHostOperation(String mockDockerPath,
ContainerHostOperationType operationType) throws Throwable {
mockDockerHost.waitForServiceAvailable(MockDockerHostService.SELF_LINK + mockDockerPath);

sendContainerHostRequest(operationType,
UriUtils.buildUri(host, dockerHostState.documentSelfLink));

// wait for provisioning task stage to change to finish
waitForPropertyValue(provisioningTaskLink, MockTaskState.class, TASK_INFO_STAGE,
TaskState.TaskStage.FINISHED);

dockerHostState = retrieveDockerHostState();

return dockerHostState;
}

protected ComputeState retrieveDockerHostState() throws Throwable {
Operation getContainerState = Operation.createGet(UriUtils.buildUri(host,
dockerHostState.documentSelfLink))
.setCompletion((o, ex) -> {
if (ex != null) {
host.failIteration(ex);

} else {
dockerHostState = o.getBody(ComputeState.class);
host.completeIteration();
}
});

host.testStart(1);
host.send(getContainerState);
host.testWait();

return dockerHostState;
}

protected void sendContainerHostRequest(ContainerHostOperationType type, URI computeStateReference)
throws Throwable {
ContainerHostRequest request = new ContainerHostRequest();
request.resourceReference = computeStateReference;
request.operationTypeId = type.id;
request.serviceTaskCallback = ServiceTaskCallback.create(provisioningTaskLink);

sendContainerHostRequest(request);
}

protected void sendContainerHostRequest(ContainerHostRequest request) throws Throwable {

Operation startContainer = Operation
.createPatch(dockerHostAdapterServiceUri)
.setReferer(URI.create("/")).setBody(request)
.setCompletion((o, ex) -> {
if (ex != null) {
host.failIteration(ex);
}

host.completeIteration();
});

host.testStart(1);
host.send(startContainer);
host.testWait();

if (!isMockTarget()) {
// in case of testing with a real docker server, give it some time to settle
Thread.sleep(100L);
}
}

protected void createTestDockerAuthCredentials() throws Throwable {
testDockerCredentialsLink = doPost(getDockerCredentials(),
AuthCredentialsService.FACTORY_LINK).documentSelfLink;
SslTrustCertificateState dockerServerTrust = getDockerServerTrust();
if (dockerServerTrust != null && dockerServerTrust.certificate != null
&& !dockerServerTrust.certificate.isEmpty()) {
doPost(dockerServerTrust, SslTrustCertificateService.FACTORY_LINK);
}
}

protected void createProvisioningTask() throws Throwable {
MockTaskState provisioningTask = new MockTaskState();
provisioningTaskLink = doPost(provisioningTask,
MockTaskFactoryService.SELF_LINK).documentSelfLink;
}

/**
* Get a property value from system properties or the given properties file
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* Copyright (c) 2017 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.adapter.docker.mock;

import static com.vmware.admiral.adapter.docker.mock.MockDockerPathConstants.BASE_VERSIONED_PATH;
import static com.vmware.admiral.adapter.docker.mock.MockDockerPathConstants.INFO;
import static com.vmware.admiral.adapter.docker.mock.MockDockerPathConstants.VERSION;
import static com.vmware.admiral.adapter.docker.mock.MockDockerPathConstants._PING;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.vmware.xenon.common.Operation;
import com.vmware.xenon.common.Service;
import com.vmware.xenon.common.ServiceDocument;
import com.vmware.xenon.common.StatefulService;
import com.vmware.xenon.common.UriUtils;

public class MockVicHostService extends StatefulService {

public static final String SELF_LINK = BASE_VERSIONED_PATH;
public static final String[] childPaths = { INFO, _PING, VERSION };
public static String NUMBER_OF_CONTAINERS = "11";

private final List<Service> childServices = new ArrayList<>();

public MockVicHostService() {
super(ServiceDocument.class);
}

@Override
public void handleStart(Operation post) {
// create child endpoints (for version, info ..) using a child service
if (!isChildService(post)) {
for (String childPath : childPaths) {
startChildService(post, childPath, new MockVicHostService());
}
}

post.complete();
}

/**
* Is the current service a child service
*
* @return
*/
private boolean isChildService(Operation op) {
String path = op.getUri().getPath();
return !path.equals(getParentPath(op));
}

/**
* Get the path of the parent service
*
* @param op
* @return
*/
private String getParentPath(Operation op) {
String path = op.getUri().getPath();
for (String childPath : childPaths) {
int index = path.lastIndexOf(childPath);
if (index > 0) {
return path.substring(0, index);
}
}
return path;
}

private void startChildService(Operation post, String path, Service childService) {
getHost().startService(
Operation.createPost(UriUtils.extendUri(post.getUri(), path))
.setBody(post.getBodyRaw()), childService
);

childServices.add(childService);
}

@Override
public void handleDelete(Operation delete) {
for (Service childService : childServices) {
// delete child service as well
getHost().stopService(childService);
}
super.handleDelete(delete);
}

@Override
public void handleGet(Operation op) {
String currentPath = op.getUri().getPath();
if (currentPath.endsWith(INFO)) {
op.setBody(getInfoBody());
op.complete();
} else {
op.fail(new IllegalArgumentException("Not supported."));
}
}

private Map<String, Object> getInfoBody() {
Map<String, Object> body = new HashMap<>();

body.put("Containers", NUMBER_OF_CONTAINERS);
body.put("CpuCfsPeriod", "true");
body.put("CpuCfsQuota", "true");
body.put("DockerRootDir", "/var/lib/docker");
body.put("HttpProxy", "http://test:test@localhost:8080");
body.put("HttpsProxy", "https://test:test@localhost:8080");
body.put("ID", "7TRN:IPZB:QYBB:VPBQ:UMPP:KARE:6ZNR:XE6T:7EWV:PKF4:ZOJD:TPYS");
body.put("IPv4Forwarding", "true");
body.put("Images", "16");
body.put("IndexServerAddress", "https://index.docker.io/v1/");
body.put("InitPath", "/usr/bin/docker");
body.put("KernelVersion", "3.12.0-1-amd64");
body.put("MemTotal", "6979321856");
body.put("MemoryLimit", "true");
body.put("NCPU", "1");
body.put("Name", "prod-server-42");
body.put("NoProxy", "9.81.1.160");
body.put("OperatingSystem", "Boot2Docker");
body.put("SwapLimit", "false");
body.put("SystemTime", "2015-03-10T11:11:23.730591467-07:00");
body.put("Driver", "vSphere Integrated Containers");
List<List<String>> systemStatus = new ArrayList<>();
systemStatus.add(Arrays.asList("VolumeStores", "default"));
systemStatus.add(Arrays.asList(" VCH CPU limit", "7500 MHz"));
systemStatus.add(Arrays.asList(" VCH memory limit", "6.500 GiB"));
systemStatus.add(Arrays.asList(" VCH CPU usage", "2250 MHz"));
systemStatus.add(Arrays.asList(" VCH memory usage", "2.500 GiB"));
body.put("SystemStatus", systemStatus);
return body;
}
}
Loading

0 comments on commit 547ebd6

Please sign in to comment.