io.mesosphere.mesos.frameworks.cassandra.scheduler.api.NodeController.java Source code

Java tutorial

Introduction

Here is the source code for io.mesosphere.mesos.frameworks.cassandra.scheduler.api.NodeController.java

Source

/**
 *    Copyright (C) 2015 Mesosphere, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.mesosphere.mesos.frameworks.cassandra.scheduler.api;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import io.mesosphere.mesos.frameworks.cassandra.CassandraFrameworkProtos;
import io.mesosphere.mesos.frameworks.cassandra.scheduler.*;
import io.mesosphere.mesos.frameworks.cassandra.scheduler.util.JaxRsUtils;
import org.jetbrains.annotations.NotNull;

import javax.ws.rs.*;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.util.List;

@Path("/node")
@Produces("application/json")
public final class NodeController {

    @NotNull
    private final CassandraCluster cluster;
    @NotNull
    private final JsonFactory factory;

    public NodeController(@NotNull final CassandraCluster cluster, @NotNull final JsonFactory factory) {
        this.cluster = cluster;
        this.factory = factory;
    }

    /**
     * Retrieve a list of all nodes including their status.
     *
     *     <pre>{@code {
     * "replaceNodes" : [ ],
     * "nodesToAcquire" : 0,
     * "nodes" : [ {
     *     "tasks" : {
     *         "METADATA" : {
     *             "cpuCores" : 0.1,
     *             "diskMb" : 16,
     *             "memMb" : 16,
     *             "taskId" : "cassandra.node.0.executor"
     *         },
     *         "SERVER" : {
     *             "cpuCores" : 2.0,
     *             "diskMb" : 2048,
     *             "memMb" : 2048,
     *             "taskId" : "cassandra.node.0.executor.server"
     *         }
     *     },
     *     "executorId" : "cassandra.node.0.executor",
     *     "ip" : "127.0.0.2",
     *     "hostname" : "127.0.0.2",
     *     "targetRunState" : "RUN",
     *     "jmxPort" : 64112,
     *     "seedNode" : true,
     *     "cassandraDaemonPid" : 6104,
     *     "lastHealthCheck" : 1426686217128,
     *     "healthCheckDetails" : {
     *         "healthy" : true,
     *         "msg" : "",
     *         "version" : "2.1.4",
     *         "operationMode" : "NORMAL",
     *         "clusterName" : "cassandra",
     *         "dataCenter" : "DC1",
     *         "rack" : "RAC1",
     *         "endpoint" : "127.0.0.2",
     *         "hostId" : "4207396e-6aa0-432e-97d9-1a4df3c1057f",
     *         "joined" : true,
     *         "gossipInitialized" : true,
     *         "gossipRunning" : true,
     *         "nativeTransportRunning" : true,
     *         "rpcServerRunning" : true,
     *         "tokenCount" : 256,
     *         "uptimeMillis" : 29072
     *     }
     * }, {
     *     "tasks" : {
     *     "METADATA" : {
     *         "cpuCores" : 0.1,
     *         "diskMb" : 16,
     *         "memMb" : 16,
     *         "taskId" : "cassandra.node.1.executor"
     *     },
     *     "SERVER" : {
     *         "cpuCores" : 2.0,
     *         "diskMb" : 2048,
     *         "memMb" : 2048,
     *         "taskId" : "cassandra.node.1.executor.server"
     *     }
     *     },
     *     "executorId" : "cassandra.node.1.executor",
     *     "ip" : "127.0.0.1",
     *     "hostname" : "localhost",
     *     "targetRunState" : "RUN",
     *     "jmxPort" : 64113,
     *     "seedNode" : false,
     *     "cassandraDaemonPid" : 6127,
     *     "lastHealthCheck" : 1426686217095,
     *     "healthCheckDetails" : {
     *         "healthy" : true,
     *         "msg" : "",
     *         "version" : "2.1.4",
     *         "operationMode" : "JOINING",
     *         "clusterName" : "cassandra",
     *         "dataCenter" : "",
     *         "rack" : "",
     *         "endpoint" : "",
     *         "hostId" : "",
     *         "joined" : true,
     *         "gossipInitialized" : true,
     *         "gossipRunning" : true,
     *         "nativeTransportRunning" : false,
     *         "rpcServerRunning" : false,
     *         "tokenCount" : 0,
     *         "uptimeMillis" : 16936
     *     }
     * } ]
     * }}</pre>
     */
    @GET
    @Path("/all")
    public Response nodes() {
        return JaxRsUtils.buildStreamingResponse(factory, new StreamingJsonResponse() {
            @Override
            public void write(final JsonGenerator json) throws IOException {
                final CassandraFrameworkProtos.CassandraClusterState clusterState = cluster.getClusterState().get();

                json.writeArrayFieldStart("replaceNodes");
                for (final String ip : clusterState.getReplaceNodeIpsList()) {
                    json.writeString(ip);
                }
                json.writeEndArray();

                final NodeCounts nodeCounts = cluster.getClusterState().nodeCounts();
                json.writeNumberField("nodesToAcquire",
                        CassandraCluster.numberOfNodesToAcquire(nodeCounts, cluster.getConfiguration()));

                json.writeArrayFieldStart("nodes");
                for (final CassandraFrameworkProtos.CassandraNode cassandraNode : clusterState.getNodesList()) {
                    json.writeStartObject();

                    if (cassandraNode.hasReplacementForIp()) {
                        json.writeStringField("replacementForIp", cassandraNode.getReplacementForIp());
                    }

                    json.writeObjectFieldStart("tasks");
                    for (final CassandraFrameworkProtos.CassandraNodeTask cassandraNodeTask : cassandraNode
                            .getTasksList()) {
                        JaxRsUtils.writeTask(json, cassandraNodeTask);
                    }
                    json.writeEndObject();
                    // TODO                cassandraNode.getDataVolumesList();

                    if (!cassandraNode.hasCassandraNodeExecutor()) {
                        json.writeNullField("executorId");
                        json.writeNullField("workdir");
                    } else {
                        json.writeStringField("executorId",
                                cassandraNode.getCassandraNodeExecutor().getExecutorId());
                        final CassandraFrameworkProtos.ExecutorMetadata executorMetadata = cluster
                                .metadataForExecutor(cassandraNode.getCassandraNodeExecutor().getExecutorId());
                        if (executorMetadata != null) {
                            json.writeStringField("workdir", executorMetadata.getWorkdir());
                        } else {
                            json.writeNullField("workdir");
                        }
                    }
                    json.writeStringField("ip", cassandraNode.getIp());
                    json.writeStringField("hostname", cassandraNode.getHostname());
                    json.writeStringField("targetRunState", cassandraNode.getTargetRunState().name());
                    json.writeNumberField("jmxPort", cassandraNode.getJmxConnect().getJmxPort());
                    json.writeBooleanField("seedNode", cassandraNode.getSeed());

                    CassandraFrameworkProtos.RackDc rackDc = cassandraNode.getRackDc();
                    json.writeObjectFieldStart("rackDc");
                    json.writeStringField("rack", rackDc.getRack());
                    json.writeStringField("dc", rackDc.getDc());
                    json.writeEndObject();

                    if (!cassandraNode.hasCassandraDaemonPid()) {
                        json.writeNullField("cassandraDaemonPid");
                    } else {
                        json.writeNumberField("cassandraDaemonPid", cassandraNode.getCassandraDaemonPid());
                    }

                    final CassandraFrameworkProtos.HealthCheckHistoryEntry lastHealthCheck = cassandraNode
                            .hasCassandraNodeExecutor()
                                    ? cluster.lastHealthCheck(
                                            cassandraNode.getCassandraNodeExecutor().getExecutorId())
                                    : null;

                    if (lastHealthCheck != null) {
                        json.writeNumberField("lastHealthCheck", lastHealthCheck.getTimestampEnd());
                    } else {
                        json.writeNullField("lastHealthCheck");
                    }

                    if (lastHealthCheck != null) {
                        json.writeObjectFieldStart("healthCheckDetails");

                        final CassandraFrameworkProtos.HealthCheckDetails hcd = lastHealthCheck.getDetails();

                        json.writeBooleanField("healthy", hcd.getHealthy());
                        json.writeStringField("msg", hcd.getMsg());

                        json.writeStringField("version", hcd.getInfo().getVersion());
                        json.writeStringField("operationMode", hcd.getInfo().getOperationMode());
                        json.writeStringField("clusterName", hcd.getInfo().getClusterName());
                        json.writeStringField("dataCenter", hcd.getInfo().getDataCenter());
                        json.writeStringField("rack", hcd.getInfo().getRack());
                        json.writeStringField("endpoint", hcd.getInfo().getEndpoint());
                        json.writeStringField("hostId", hcd.getInfo().getHostId());
                        json.writeBooleanField("joined", hcd.getInfo().getJoined());
                        json.writeBooleanField("gossipInitialized", hcd.getInfo().getGossipInitialized());
                        json.writeBooleanField("gossipRunning", hcd.getInfo().getGossipRunning());
                        json.writeBooleanField("nativeTransportRunning", hcd.getInfo().getNativeTransportRunning());
                        json.writeBooleanField("rpcServerRunning", hcd.getInfo().getRpcServerRunning());
                        json.writeNumberField("tokenCount", hcd.getInfo().getTokenCount());
                        json.writeNumberField("uptimeMillis", hcd.getInfo().getUptimeMillis());

                        json.writeEndObject();
                    } else {
                        json.writeNullField("healthCheckDetails");
                    }

                    final List<CassandraFrameworkProtos.DataVolume> dataVolumes = cassandraNode
                            .getDataVolumesList();
                    json.writeArrayFieldStart("dataVolumes");
                    for (final CassandraFrameworkProtos.DataVolume volume : dataVolumes) {
                        json.writeStartObject();
                        json.writeStringField("path", volume.getPath());
                        if (volume.hasSizeMb()) {
                            json.writeNumberField("size", volume.getSizeMb());
                        }
                        json.writeEndObject();
                    }
                    json.writeEndArray();

                    json.writeEndObject();
                }
                json.writeEndArray();
            }
        });
    }

    /**
     * Returns a JSON with the IP addresses of all seed nodes and native, thrift and JMX port numbers.
     *
     * Example:
     * <pre>{@code {
     * "nativePort" : 9042,
     * "rpcPort" : 9160,
     * "jmxPort" : 7199,
     * "seeds" : [ "127.0.0.1" ]
     * }}</pre>
     */
    @GET
    @Path("/seed/all")
    public Response seedNodes() {
        final CassandraFrameworkProtos.CassandraFrameworkConfiguration configuration = cluster.getConfiguration()
                .get();
        return JaxRsUtils.buildStreamingResponse(factory, new StreamingJsonResponse() {
            @Override
            public void write(final JsonGenerator json) throws IOException {

                json.writeNumberField("nativePort",
                        CassandraCluster.getPortMapping(configuration, CassandraCluster.PORT_NATIVE));
                json.writeNumberField("rpcPort",
                        CassandraCluster.getPortMapping(configuration, CassandraCluster.PORT_RPC));
                json.writeNumberField("jmxPort",
                        CassandraCluster.getPortMapping(configuration, CassandraCluster.PORT_JMX));

                JaxRsUtils.writeSeedIps(cluster, json);
            }
        });
    }

    /**
     * Allows to make a non-seed node a seed node. The node is specified using the path parameter `node`.
     * The `node` parameter can be either the IP address, the hostname or the executor ID.
     * Must be submitted using HTTP method {@code POST}.
     *
     * Example: <pre>{@code {
     * "ip" : "127.0.0.1",
     * "hostname" : "localhost",
     * "executorId" : "cassandra.node.1.executor",
     * "oldSeedState" : "false",
     * "success" : "false",
     * "error" : "Some error message"
     * }}</pre>
     *
     * <pre>{@code {
     * "ip" : "127.0.0.1",
     * "hostname" : "localhost",
     * "executorId" : "cassandra.node.1.executor",
     * "oldSeedState" : "false",
     * "success" : "true",
     * "seedState" : "true"
     * }}</pre>
     */
    @POST
    @Path("/{node}/make-seed")
    public Response nodeMakeSeed(@PathParam("node") final String node) {
        return nodeUpdateSeed(node, true);
    }

    /**
     * Allows to make a seed node a non-seed node. The node is specified using the path parameter `node`.
     * The `node` parameter can be either the IP address, the hostname or the executor ID.
     * Must be submitted using HTTP method {@code POST}.
     *
     * Example: <pre>{@code {
     * "ip" : "127.0.0.1",
     * "hostname" : "localhost",
     * "executorId" : "cassandra.node.1.executor",
     * "oldSeedState" : "true",
     * "success" : "false",
     * "error" : "Some error message"
     * }}</pre>
     *
     * <pre>{@code {
     * "ip" : "127.0.0.1",
     * "hostname" : "localhost",
     * "executorId" : "cassandra.node.1.executor",
     * "oldSeedState" : "true",
     * "success" : "true",
     * "seedState" : "false"
     * }}</pre>
     */
    @POST
    @Path("/{node}/make-non-seed")
    public Response nodeMakeNonSeed(@PathParam("node") final String node) {
        return nodeUpdateSeed(node, false);
    }

    private Response nodeUpdateSeed(final String node, final boolean seed) {
        final CassandraFrameworkProtos.CassandraNode cassandraNode = cluster.findNode(node);
        if (cassandraNode == null) {
            return Response.status(404).build();
        }

        try {
            final boolean seedChanged = cluster.setNodeSeed(cassandraNode, seed);

            return JaxRsUtils.buildStreamingResponse(factory, new StreamingJsonResponse() {
                @Override
                public void write(final JsonGenerator json) throws IOException {
                    json.writeStringField("ip", cassandraNode.getIp());
                    json.writeStringField("hostname", cassandraNode.getHostname());
                    if (!cassandraNode.hasCassandraNodeExecutor()) {
                        json.writeNullField("executorId");
                    } else {
                        json.writeStringField("executorId",
                                cassandraNode.getCassandraNodeExecutor().getExecutorId());
                    }
                    json.writeBooleanField("oldSeedState", cassandraNode.getSeed());

                    if (seedChanged) {
                        json.writeBooleanField("success", true);
                        json.writeBooleanField("seedState", seed);
                    } else {
                        json.writeBooleanField("success", false);
                        json.writeBooleanField("seedState", cassandraNode.getSeed());
                    }

                }
            });
        } catch (final SeedChangeException e) {
            return JaxRsUtils.buildStreamingResponse(factory, Response.Status.BAD_REQUEST,
                    new StreamingJsonResponse() {
                        @Override
                        public void write(final JsonGenerator json) throws IOException {
                            json.writeStringField("ip", cassandraNode.getIp());
                            json.writeStringField("hostname", cassandraNode.getHostname());
                            if (!cassandraNode.hasCassandraNodeExecutor()) {
                                json.writeNullField("executorId");
                            } else {
                                json.writeStringField("executorId",
                                        cassandraNode.getCassandraNodeExecutor().getExecutorId());
                            }
                            json.writeBooleanField("oldSeedState", cassandraNode.getSeed());

                            json.writeBooleanField("success", false);
                            json.writeStringField("error", e.getMessage());
                        }
                    });
        }

    }

    /**
     * Sets requested state of the Cassandra process to 'stop' for the node specified using the path parameter `node`.
     * The `node` parameter can be either the IP address, the hostname or the executor ID.
     * The request must be submitted using HTTP method `POST`.
     */
    @POST
    @Path("/{node}/stop")
    public Response nodeStop(@PathParam("node") final String node) {
        final CassandraFrameworkProtos.CassandraNode cassandraNode = cluster.nodeStop(node);

        return nodeStatusUpdate(cassandraNode);
    }

    /**
     * Sets requested state of the Cassandra process to 'run' for the node specified using the path parameter `node`.
     * The `node` parameter can be either the IP address, the hostname or the executor ID.
     * The request must be submitted using HTTP method `POST`.
     */
    @POST
    @Path("/{node}/start")
    public Response nodeStart(@PathParam("node") final String node) {
        final CassandraFrameworkProtos.CassandraNode cassandraNode = cluster.nodeRun(node);

        return nodeStatusUpdate(cassandraNode);
    }

    /**
     * Sets requested state of the Cassandra process to 'restart' for the node specified using the path parameter `node`.
     * The `node` parameter can be either the IP address, the hostname or the executor ID.
     * The request must be submitted using HTTP method `POST`.
     */
    @POST
    @Path("/{node}/restart")
    public Response nodeRestart(@PathParam("node") final String node) {
        final CassandraFrameworkProtos.CassandraNode cassandraNode = cluster.nodeRestart(node);

        return nodeStatusUpdate(cassandraNode);
    }

    /**
     * Sets requested state of the Cassandra process to 'terminate' for the node specified using the path parameter `node`.
     * The `node` parameter can be either the IP address, the hostname or the executor ID.
     * The request must be submitted using HTTP method `POST`.
     * Note that a terminated node cannot be restarted.
     */
    @POST
    @Path("/{node}/terminate")
    public Response nodeTerminate(@PathParam("node") final String node) {
        final CassandraFrameworkProtos.CassandraNode cassandraNode = cluster.nodeTerminate(node);

        return nodeStatusUpdate(cassandraNode);
    }

    /**
     * Submit intent to replace the already terminated node specified using the path parameter `node`.
     * The `node` parameter can be either the IP address, the hostname or the executor ID.
     * The request must be submitted using HTTP method `POST`.
     */
    @POST
    @Path("/{node}/replace")
    public Response nodeReplace(@PathParam("node") final String node) {
        final CassandraFrameworkProtos.CassandraNode cassandraNode = cluster.findNode(node);

        if (cassandraNode == null) {
            return JaxRsUtils.buildStreamingResponse(factory, Response.Status.NOT_FOUND,
                    new StreamingJsonResponse() {
                        @Override
                        public void write(final JsonGenerator json) throws IOException {
                            json.writeBooleanField("success", false);
                            json.writeStringField("reason", "No such node");
                        }
                    });
        }

        try {
            cluster.replaceNode(node);
        } catch (final ReplaceNodePreconditionFailed replaceNodePreconditionFailed) {
            return JaxRsUtils.buildStreamingResponse(factory, Response.Status.BAD_REQUEST,
                    new StreamingJsonResponse() {
                        @Override
                        public void write(final JsonGenerator json) throws IOException {
                            json.writeStringField("ipToReplace", cassandraNode.getIp());
                            json.writeBooleanField("success", false);
                            json.writeStringField("reason", "No such node");
                        }
                    });
        }

        return JaxRsUtils.buildStreamingResponse(factory, new StreamingJsonResponse() {
            @Override
            public void write(final JsonGenerator json) throws IOException {
                json.writeStringField("ipToReplace", cassandraNode.getIp());
                json.writeBooleanField("success", true);
                json.writeStringField("hostname", cassandraNode.getHostname());
                json.writeStringField("targetRunState", cassandraNode.getTargetRunState().name());
            }
        });
    }

    public static class RackDcParams {
        public String rack;
        public String dc;
    }

    /**
     * Update node with specified parameters. Note: node should be restarted for changes to take effect.
     */
    @POST
    @Path("/{node}/rackdc")
    public Response nodeRackDc(@PathParam("node") String id, RackDcParams params) {
        CassandraFrameworkProtos.CassandraNode node = cluster.findNode(id);
        if (node == null)
            return Response.status(404).build();

        final CassandraFrameworkProtos.CassandraNode.Builder copy = CassandraFrameworkProtos.CassandraNode
                .newBuilder(node);
        CassandraFrameworkProtos.RackDc.Builder rackDc = CassandraFrameworkProtos.RackDc
                .newBuilder(node.getRackDc());

        if (params.rack != null)
            rackDc.setRack(params.rack);
        if (params.dc != null)
            rackDc.setDc(params.dc);

        copy.setRackDc(rackDc);
        cluster.getClusterState().addOrSetNode(copy.build());

        return JaxRsUtils.buildStreamingResponse(factory, new StreamingJsonResponse() {
            @Override
            public void write(final JsonGenerator json) throws IOException {
                json.writeBooleanField("success", true);
                json.writeStringField("rack", copy.getRackDc().getRack());
                json.writeStringField("dc", copy.getRackDc().getDc());
            }
        });
    }

    private Response nodeStatusUpdate(final CassandraFrameworkProtos.CassandraNode cassandraNode) {
        if (cassandraNode == null) {
            return Response.status(Response.Status.NOT_FOUND).build();
        }

        return JaxRsUtils.buildStreamingResponse(factory, new StreamingJsonResponse() {
            @Override
            public void write(final JsonGenerator json) throws IOException {
                json.writeStringField("ip", cassandraNode.getIp());
                json.writeStringField("hostname", cassandraNode.getHostname());
                if (!cassandraNode.hasCassandraNodeExecutor()) {
                    json.writeNullField("executorId");
                } else {
                    json.writeStringField("executorId", cassandraNode.getCassandraNodeExecutor().getExecutorId());
                }
                json.writeStringField("targetRunState", cassandraNode.getTargetRunState().name());
            }
        });
    }
}