blob: a1ada8fe5c320c0617802bac9d68110812abf6db [file] [log] [blame]
/*
* Copyright 2018 The gRPC Authors
*
* 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.grpc.services;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.protobuf.Any;
import com.google.protobuf.ByteString;
import com.google.protobuf.Int64Value;
import com.google.protobuf.util.Durations;
import com.google.protobuf.util.Timestamps;
import io.grpc.ConnectivityState;
import io.grpc.InternalChannelz;
import io.grpc.InternalChannelz.ChannelStats;
import io.grpc.InternalChannelz.ChannelTrace.Event;
import io.grpc.InternalChannelz.RootChannelList;
import io.grpc.InternalChannelz.ServerList;
import io.grpc.InternalChannelz.ServerSocketsList;
import io.grpc.InternalChannelz.ServerStats;
import io.grpc.InternalChannelz.SocketStats;
import io.grpc.InternalChannelz.TransportStats;
import io.grpc.InternalInstrumented;
import io.grpc.InternalWithLogId;
import io.grpc.Status;
import io.grpc.channelz.v1.Address;
import io.grpc.channelz.v1.Address.OtherAddress;
import io.grpc.channelz.v1.Address.TcpIpAddress;
import io.grpc.channelz.v1.Address.UdsAddress;
import io.grpc.channelz.v1.Channel;
import io.grpc.channelz.v1.ChannelConnectivityState;
import io.grpc.channelz.v1.ChannelConnectivityState.State;
import io.grpc.channelz.v1.ChannelData;
import io.grpc.channelz.v1.ChannelRef;
import io.grpc.channelz.v1.ChannelTrace;
import io.grpc.channelz.v1.ChannelTraceEvent;
import io.grpc.channelz.v1.ChannelTraceEvent.Severity;
import io.grpc.channelz.v1.GetServerSocketsResponse;
import io.grpc.channelz.v1.GetServersResponse;
import io.grpc.channelz.v1.GetTopChannelsResponse;
import io.grpc.channelz.v1.Security;
import io.grpc.channelz.v1.Security.OtherSecurity;
import io.grpc.channelz.v1.Security.Tls;
import io.grpc.channelz.v1.Server;
import io.grpc.channelz.v1.ServerData;
import io.grpc.channelz.v1.ServerRef;
import io.grpc.channelz.v1.Socket;
import io.grpc.channelz.v1.Socket.Builder;
import io.grpc.channelz.v1.SocketData;
import io.grpc.channelz.v1.SocketOption;
import io.grpc.channelz.v1.SocketOptionLinger;
import io.grpc.channelz.v1.SocketOptionTcpInfo;
import io.grpc.channelz.v1.SocketOptionTimeout;
import io.grpc.channelz.v1.SocketRef;
import io.grpc.channelz.v1.Subchannel;
import io.grpc.channelz.v1.SubchannelRef;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.security.cert.CertificateEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A static utility class for turning internal data structures into protos.
*/
final class ChannelzProtoUtil {
private static final Logger logger = Logger.getLogger(ChannelzProtoUtil.class.getName());
private ChannelzProtoUtil() {
// do not instantiate.
}
static ChannelRef toChannelRef(InternalWithLogId obj) {
return ChannelRef
.newBuilder()
.setChannelId(obj.getLogId().getId())
.setName(obj.toString())
.build();
}
static SubchannelRef toSubchannelRef(InternalWithLogId obj) {
return SubchannelRef
.newBuilder()
.setSubchannelId(obj.getLogId().getId())
.setName(obj.toString())
.build();
}
static ServerRef toServerRef(InternalWithLogId obj) {
return ServerRef
.newBuilder()
.setServerId(obj.getLogId().getId())
.setName(obj.toString())
.build();
}
static SocketRef toSocketRef(InternalWithLogId obj) {
return SocketRef
.newBuilder()
.setSocketId(obj.getLogId().getId())
.setName(obj.toString())
.build();
}
static Server toServer(InternalInstrumented<ServerStats> obj) {
ServerStats stats = getFuture(obj.getStats());
Server.Builder builder = Server
.newBuilder()
.setRef(toServerRef(obj))
.setData(toServerData(stats));
for (InternalInstrumented<SocketStats> listenSocket : stats.listenSockets) {
builder.addListenSocket(toSocketRef(listenSocket));
}
return builder.build();
}
static ServerData toServerData(ServerStats stats) {
return ServerData
.newBuilder()
.setCallsStarted(stats.callsStarted)
.setCallsSucceeded(stats.callsSucceeded)
.setCallsFailed(stats.callsFailed)
.setLastCallStartedTimestamp(Timestamps.fromNanos(stats.lastCallStartedNanos))
.build();
}
static Security toSecurity(InternalChannelz.Security security) {
Preconditions.checkNotNull(security);
Preconditions.checkState(
security.tls != null ^ security.other != null,
"one of tls or othersecurity must be non null");
if (security.tls != null) {
Tls.Builder tlsBuilder
= Tls.newBuilder().setStandardName(security.tls.cipherSuiteStandardName);
try {
if (security.tls.localCert != null) {
tlsBuilder.setLocalCertificate(ByteString.copyFrom(
security.tls.localCert.getEncoded()));
}
if (security.tls.remoteCert != null) {
tlsBuilder.setRemoteCertificate(ByteString.copyFrom(
security.tls.remoteCert.getEncoded()));
}
} catch (CertificateEncodingException e) {
logger.log(Level.FINE, "Caught exception", e);
}
return Security.newBuilder().setTls(tlsBuilder).build();
} else {
OtherSecurity.Builder builder = OtherSecurity.newBuilder().setName(security.other.name);
if (security.other.any != null) {
builder.setValue((Any) security.other.any);
}
return Security.newBuilder().setOther(builder).build();
}
}
static Socket toSocket(InternalInstrumented<SocketStats> obj) {
SocketStats socketStats = getFuture(obj.getStats());
Builder builder = Socket.newBuilder()
.setRef(toSocketRef(obj))
.setLocal(toAddress(socketStats.local));
if (socketStats.security != null) {
builder.setSecurity(toSecurity(socketStats.security));
}
// listen sockets do not have remote nor data
if (socketStats.remote != null) {
builder.setRemote(toAddress(socketStats.remote));
}
builder.setData(extractSocketData(socketStats));
return builder.build();
}
static Address toAddress(SocketAddress address) {
Preconditions.checkNotNull(address);
Address.Builder builder = Address.newBuilder();
if (address instanceof InetSocketAddress) {
InetSocketAddress inetAddress = (InetSocketAddress) address;
builder.setTcpipAddress(
TcpIpAddress
.newBuilder()
.setIpAddress(
ByteString.copyFrom(inetAddress.getAddress().getAddress()))
.setPort(inetAddress.getPort())
.build());
} else if (address.getClass().getName().endsWith("io.netty.channel.unix.DomainSocketAddress")) {
builder.setUdsAddress(
UdsAddress
.newBuilder()
.setFilename(address.toString()) // DomainSocketAddress.toString returns filename
.build());
} else {
builder.setOtherAddress(OtherAddress.newBuilder().setName(address.toString()).build());
}
return builder.build();
}
static SocketData extractSocketData(SocketStats socketStats) {
SocketData.Builder builder = SocketData.newBuilder();
if (socketStats.data != null) {
TransportStats s = socketStats.data;
builder
.setStreamsStarted(s.streamsStarted)
.setStreamsSucceeded(s.streamsSucceeded)
.setStreamsFailed(s.streamsFailed)
.setMessagesSent(s.messagesSent)
.setMessagesReceived(s.messagesReceived)
.setKeepAlivesSent(s.keepAlivesSent)
.setLastLocalStreamCreatedTimestamp(
Timestamps.fromNanos(s.lastLocalStreamCreatedTimeNanos))
.setLastRemoteStreamCreatedTimestamp(
Timestamps.fromNanos(s.lastRemoteStreamCreatedTimeNanos))
.setLastMessageSentTimestamp(
Timestamps.fromNanos(s.lastMessageSentTimeNanos))
.setLastMessageReceivedTimestamp(
Timestamps.fromNanos(s.lastMessageReceivedTimeNanos))
.setLocalFlowControlWindow(
Int64Value.of(s.localFlowControlWindow))
.setRemoteFlowControlWindow(
Int64Value.of(s.remoteFlowControlWindow));
}
builder.addAllOption(toSocketOptionsList(socketStats.socketOptions));
return builder.build();
}
public static final String SO_LINGER = "SO_LINGER";
public static final String SO_TIMEOUT = "SO_TIMEOUT";
public static final String TCP_INFO = "TCP_INFO";
static SocketOption toSocketOptionLinger(int lingerSeconds) {
final SocketOptionLinger lingerOpt;
if (lingerSeconds >= 0) {
lingerOpt = SocketOptionLinger
.newBuilder()
.setActive(true)
.setDuration(Durations.fromSeconds(lingerSeconds))
.build();
} else {
lingerOpt = SocketOptionLinger.getDefaultInstance();
}
return SocketOption
.newBuilder()
.setName(SO_LINGER)
.setAdditional(Any.pack(lingerOpt))
.build();
}
static SocketOption toSocketOptionTimeout(String name, int timeoutMillis) {
Preconditions.checkNotNull(name);
return SocketOption
.newBuilder()
.setName(name)
.setAdditional(
Any.pack(
SocketOptionTimeout
.newBuilder()
.setDuration(Durations.fromMillis(timeoutMillis))
.build()))
.build();
}
static SocketOption toSocketOptionTcpInfo(InternalChannelz.TcpInfo i) {
SocketOptionTcpInfo tcpInfo = SocketOptionTcpInfo.newBuilder()
.setTcpiState(i.state)
.setTcpiCaState(i.caState)
.setTcpiRetransmits(i.retransmits)
.setTcpiProbes(i.probes)
.setTcpiBackoff(i.backoff)
.setTcpiOptions(i.options)
.setTcpiSndWscale(i.sndWscale)
.setTcpiRcvWscale(i.rcvWscale)
.setTcpiRto(i.rto)
.setTcpiAto(i.ato)
.setTcpiSndMss(i.sndMss)
.setTcpiRcvMss(i.rcvMss)
.setTcpiUnacked(i.unacked)
.setTcpiSacked(i.sacked)
.setTcpiLost(i.lost)
.setTcpiRetrans(i.retrans)
.setTcpiFackets(i.fackets)
.setTcpiLastDataSent(i.lastDataSent)
.setTcpiLastAckSent(i.lastAckSent)
.setTcpiLastDataRecv(i.lastDataRecv)
.setTcpiLastAckRecv(i.lastAckRecv)
.setTcpiPmtu(i.pmtu)
.setTcpiRcvSsthresh(i.rcvSsthresh)
.setTcpiRtt(i.rtt)
.setTcpiRttvar(i.rttvar)
.setTcpiSndSsthresh(i.sndSsthresh)
.setTcpiSndCwnd(i.sndCwnd)
.setTcpiAdvmss(i.advmss)
.setTcpiReordering(i.reordering)
.build();
return SocketOption
.newBuilder()
.setName(TCP_INFO)
.setAdditional(Any.pack(tcpInfo))
.build();
}
static SocketOption toSocketOptionAdditional(String name, String value) {
Preconditions.checkNotNull(name);
Preconditions.checkNotNull(value);
return SocketOption.newBuilder().setName(name).setValue(value).build();
}
static List<SocketOption> toSocketOptionsList(InternalChannelz.SocketOptions options) {
Preconditions.checkNotNull(options);
List<SocketOption> ret = new ArrayList<>();
if (options.lingerSeconds != null) {
ret.add(toSocketOptionLinger(options.lingerSeconds));
}
if (options.soTimeoutMillis != null) {
ret.add(toSocketOptionTimeout(SO_TIMEOUT, options.soTimeoutMillis));
}
if (options.tcpInfo != null) {
ret.add(toSocketOptionTcpInfo(options.tcpInfo));
}
for (Entry<String, String> entry : options.others.entrySet()) {
ret.add(toSocketOptionAdditional(entry.getKey(), entry.getValue()));
}
return ret;
}
static Channel toChannel(InternalInstrumented<ChannelStats> channel) {
ChannelStats stats = getFuture(channel.getStats());
Channel.Builder channelBuilder = Channel
.newBuilder()
.setRef(toChannelRef(channel))
.setData(extractChannelData(stats));
for (InternalWithLogId subchannel : stats.subchannels) {
channelBuilder.addSubchannelRef(toSubchannelRef(subchannel));
}
return channelBuilder.build();
}
static ChannelData extractChannelData(InternalChannelz.ChannelStats stats) {
ChannelData.Builder builder = ChannelData.newBuilder();
builder.setTarget(stats.target)
.setState(toChannelConnectivityState(stats.state))
.setCallsStarted(stats.callsStarted)
.setCallsSucceeded(stats.callsSucceeded)
.setCallsFailed(stats.callsFailed)
.setLastCallStartedTimestamp(Timestamps.fromNanos(stats.lastCallStartedNanos));
if (stats.channelTrace != null) {
builder.setTrace(toChannelTrace(stats.channelTrace));
}
return builder.build();
}
static ChannelConnectivityState toChannelConnectivityState(ConnectivityState s) {
return ChannelConnectivityState.newBuilder().setState(toState(s)).build();
}
private static ChannelTrace toChannelTrace(InternalChannelz.ChannelTrace channelTrace) {
return ChannelTrace.newBuilder()
.setNumEventsLogged(channelTrace.numEventsLogged)
.setCreationTimestamp(Timestamps.fromNanos(channelTrace.creationTimeNanos))
.addAllEvents(toChannelTraceEvents(channelTrace.events))
.build();
}
private static List<ChannelTraceEvent> toChannelTraceEvents(List<Event> events) {
List<ChannelTraceEvent> channelTraceEvents = new ArrayList<>();
for (Event event : events) {
ChannelTraceEvent.Builder builder = ChannelTraceEvent.newBuilder()
.setDescription(event.description)
.setSeverity(Severity.valueOf(event.severity.name()))
.setTimestamp(Timestamps.fromNanos(event.timestampNanos));
if (event.channelRef != null) {
builder.setChannelRef(toChannelRef(event.channelRef));
}
if (event.subchannelRef != null) {
builder.setSubchannelRef(toSubchannelRef(event.subchannelRef));
}
channelTraceEvents.add(builder.build());
}
return Collections.unmodifiableList(channelTraceEvents);
}
static State toState(ConnectivityState state) {
if (state == null) {
return State.UNKNOWN;
}
try {
return Enum.valueOf(State.class, state.name());
} catch (IllegalArgumentException e) {
return State.UNKNOWN;
}
}
static Subchannel toSubchannel(InternalInstrumented<ChannelStats> subchannel) {
ChannelStats stats = getFuture(subchannel.getStats());
Subchannel.Builder subchannelBuilder = Subchannel
.newBuilder()
.setRef(toSubchannelRef(subchannel))
.setData(extractChannelData(stats));
Preconditions.checkState(stats.sockets.isEmpty() || stats.subchannels.isEmpty());
for (InternalWithLogId childSocket : stats.sockets) {
subchannelBuilder.addSocketRef(toSocketRef(childSocket));
}
for (InternalWithLogId childSubchannel : stats.subchannels) {
subchannelBuilder.addSubchannelRef(toSubchannelRef(childSubchannel));
}
return subchannelBuilder.build();
}
static GetTopChannelsResponse toGetTopChannelResponse(RootChannelList rootChannels) {
GetTopChannelsResponse.Builder responseBuilder = GetTopChannelsResponse
.newBuilder()
.setEnd(rootChannels.end);
for (InternalInstrumented<ChannelStats> c : rootChannels.channels) {
responseBuilder.addChannel(ChannelzProtoUtil.toChannel(c));
}
return responseBuilder.build();
}
static GetServersResponse toGetServersResponse(ServerList servers) {
GetServersResponse.Builder responseBuilder = GetServersResponse
.newBuilder()
.setEnd(servers.end);
for (InternalInstrumented<ServerStats> s : servers.servers) {
responseBuilder.addServer(ChannelzProtoUtil.toServer(s));
}
return responseBuilder.build();
}
static GetServerSocketsResponse toGetServerSocketsResponse(ServerSocketsList serverSockets) {
GetServerSocketsResponse.Builder responseBuilder = GetServerSocketsResponse
.newBuilder()
.setEnd(serverSockets.end);
for (InternalWithLogId s : serverSockets.sockets) {
responseBuilder.addSocketRef(ChannelzProtoUtil.toSocketRef(s));
}
return responseBuilder.build();
}
private static <T> T getFuture(ListenableFuture<T> future) {
try {
T ret = future.get();
if (ret == null) {
throw Status.UNIMPLEMENTED
.withDescription("The entity's stats can not be retrieved. "
+ "If this is an InProcessTransport this is expected.")
.asRuntimeException();
}
return ret;
} catch (InterruptedException e) {
throw Status.INTERNAL.withCause(e).asRuntimeException();
} catch (ExecutionException e) {
throw Status.INTERNAL.withCause(e).asRuntimeException();
}
}
}