/*
* Licensed to Crate under one or more contributor license agreements.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership. Crate licenses this file
* to you 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.
*
* However, if you have executed another commercial license agreement
* with Crate these terms will supersede the license and you may use the
* software solely pursuant to the terms of the relevant commercial
* agreement.
*/
package io.crate.operation.reference.sys.node;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import io.crate.Build;
import io.crate.Version;
import io.crate.metadata.ColumnIdent;
import io.crate.metadata.sys.SysNodesTableInfo;
import io.crate.monitor.ExtendedNodeInfo;
import io.crate.monitor.ThreadPools;
import io.crate.protocols.postgres.PostgresNetty;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.inject.Singleton;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.lucene.BytesRefs;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.http.HttpServer;
import org.elasticsearch.monitor.MonitorService;
import org.elasticsearch.monitor.jvm.JvmService;
import org.elasticsearch.monitor.os.OsService;
import org.elasticsearch.monitor.process.ProcessService;
import org.elasticsearch.threadpool.ThreadPool;
import javax.annotation.Nullable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;
import static io.crate.operation.reference.sys.node.Ports.portFromAddress;
@Singleton
public class NodeStatsContextFieldResolver {
private final static Logger LOGGER = Loggers.getLogger(NodeStatsContextFieldResolver.class);
private final Supplier<DiscoveryNode> localNode;
private final Supplier<TransportAddress> boundHttpAddress;
private final ThreadPool threadPool;
private final ExtendedNodeInfo extendedNodeInfo;
private final Supplier<TransportAddress> boundPostgresAddress;
private final ProcessService processService;
private final OsService osService;
private final JvmService jvmService;
@Inject
public NodeStatsContextFieldResolver(ClusterService clusterService,
MonitorService monitorService,
@Nullable HttpServer httpServer,
ThreadPool threadPool,
ExtendedNodeInfo extendedNodeInfo,
PostgresNetty postgresNetty) {
this(
clusterService::localNode,
monitorService,
() -> httpServer == null ? null : httpServer.info().getAddress().publishAddress(),
threadPool,
extendedNodeInfo,
() -> postgresNetty.boundAddress().publishAddress()
);
}
@VisibleForTesting
NodeStatsContextFieldResolver(Supplier<DiscoveryNode> localNode,
MonitorService monitorService,
Supplier<TransportAddress> boundHttpAddress,
ThreadPool threadPool,
ExtendedNodeInfo extendedNodeInfo,
Supplier<TransportAddress> boundPostgresAddress) {
this.localNode = localNode;
processService = monitorService.processService();
osService = monitorService.osService();
jvmService = monitorService.jvmService();
this.boundHttpAddress = boundHttpAddress;
this.threadPool = threadPool;
this.extendedNodeInfo = extendedNodeInfo;
this.boundPostgresAddress = boundPostgresAddress;
}
public NodeStatsContext forTopColumnIdents(Collection<ColumnIdent> topColumnIdents) {
NodeStatsContext context = new NodeStatsContext(true);
if (topColumnIdents.isEmpty()) {
return context;
}
for (ColumnIdent column : topColumnIdents) {
consumerForTopColumnIdent(column).accept(context);
}
return context;
}
private Consumer<NodeStatsContext> consumerForTopColumnIdent(ColumnIdent columnIdent) {
Consumer<NodeStatsContext> consumer = columnIdentToContext.get(columnIdent);
if (consumer == null) {
throw new IllegalArgumentException(
String.format(Locale.ENGLISH, "Cannot resolve NodeStatsContext field for \"%s\" column ident.", columnIdent)
);
}
return consumer;
}
private final Map<ColumnIdent, Consumer<NodeStatsContext>> columnIdentToContext =
ImmutableMap.<ColumnIdent, Consumer<NodeStatsContext>>builder()
.put(SysNodesTableInfo.Columns.ID, new Consumer<NodeStatsContext>() {
@Override
public void accept(NodeStatsContext context) {
context.id(BytesRefs.toBytesRef(localNode.get().getId()));
}
})
.put(SysNodesTableInfo.Columns.NAME, new Consumer<NodeStatsContext>() {
@Override
public void accept(NodeStatsContext context) {
context.name(BytesRefs.toBytesRef(localNode.get().getName()));
}
})
.put(SysNodesTableInfo.Columns.HOSTNAME, context -> {
try {
context.hostname(BytesRefs.toBytesRef(InetAddress.getLocalHost().getHostName()));
} catch (UnknownHostException e) {
LOGGER.warn("Cannot resolve the hostname.", e);
}
})
.put(SysNodesTableInfo.Columns.REST_URL, new Consumer<NodeStatsContext>() {
@Override
public void accept(NodeStatsContext context) {
DiscoveryNode node = localNode.get();
if (node != null) {
String url = node.getAttributes().get("http_address");
context.restUrl(BytesRefs.toBytesRef(url));
}
}
})
.put(SysNodesTableInfo.Columns.PORT, new Consumer<NodeStatsContext>() {
@Override
public void accept(NodeStatsContext context) {
Integer http = portFromAddress(boundHttpAddress.get());
Integer transport = portFromAddress(localNode.get().getAddress());
Integer pgsql = portFromAddress(boundPostgresAddress.get());
Map<String, Integer> port = new HashMap<>(3);
port.put("http", http);
port.put("transport", transport);
port.put("psql", pgsql);
context.port(port);
}
})
.put(SysNodesTableInfo.Columns.LOAD, new Consumer<NodeStatsContext>() {
@Override
public void accept(NodeStatsContext context) {
context.extendedOsStats(extendedNodeInfo.osStats());
}
})
.put(SysNodesTableInfo.Columns.MEM, new Consumer<NodeStatsContext>() {
@Override
public void accept(NodeStatsContext context) {
context.osStats(osService.stats());
}
})
.put(SysNodesTableInfo.Columns.HEAP, new Consumer<NodeStatsContext>() {
@Override
public void accept(NodeStatsContext context) {
context.jvmStats(jvmService.stats());
}
})
.put(SysNodesTableInfo.Columns.VERSION, context -> {
context.version(Version.CURRENT);
context.build(Build.CURRENT);
})
.put(SysNodesTableInfo.Columns.THREAD_POOLS, new Consumer<NodeStatsContext>() {
@Override
public void accept(NodeStatsContext context) {
context.threadPools(ThreadPools.newInstance(threadPool));
}
})
.put(SysNodesTableInfo.Columns.NETWORK, new Consumer<NodeStatsContext>() {
@Override
public void accept(NodeStatsContext context) {
context.networkStats(extendedNodeInfo.networkStats());
}
})
.put(SysNodesTableInfo.Columns.OS, new Consumer<NodeStatsContext>() {
@Override
public void accept(NodeStatsContext context) {
context.timestamp(System.currentTimeMillis());
context.extendedOsStats(extendedNodeInfo.osStats());
}
})
.put(SysNodesTableInfo.Columns.OS_INFO, new Consumer<NodeStatsContext>() {
@Override
public void accept(NodeStatsContext context) {
context.osInfo(osService.info());
}
})
.put(SysNodesTableInfo.Columns.PROCESS, new Consumer<NodeStatsContext>() {
@Override
public void accept(NodeStatsContext context) {
context.extendedProcessCpuStats(extendedNodeInfo.processCpuStats());
context.processStats(processService.stats());
}
})
.put(SysNodesTableInfo.Columns.FS, new Consumer<NodeStatsContext>() {
@Override
public void accept(NodeStatsContext context) {
context.extendedFsStats(extendedNodeInfo.fsStats());
}
}).build();
}