• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 The gRPC Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.grpc.protobuf.services;
18 
19 import com.google.common.base.Preconditions;
20 import com.google.common.util.concurrent.ListenableFuture;
21 import com.google.protobuf.Any;
22 import com.google.protobuf.ByteString;
23 import com.google.protobuf.Int64Value;
24 import com.google.protobuf.util.Durations;
25 import com.google.protobuf.util.Timestamps;
26 import io.grpc.ConnectivityState;
27 import io.grpc.InternalChannelz;
28 import io.grpc.InternalChannelz.ChannelStats;
29 import io.grpc.InternalChannelz.ChannelTrace.Event;
30 import io.grpc.InternalChannelz.RootChannelList;
31 import io.grpc.InternalChannelz.ServerList;
32 import io.grpc.InternalChannelz.ServerSocketsList;
33 import io.grpc.InternalChannelz.ServerStats;
34 import io.grpc.InternalChannelz.SocketStats;
35 import io.grpc.InternalChannelz.TransportStats;
36 import io.grpc.InternalInstrumented;
37 import io.grpc.InternalWithLogId;
38 import io.grpc.Status;
39 import io.grpc.channelz.v1.Address;
40 import io.grpc.channelz.v1.Address.OtherAddress;
41 import io.grpc.channelz.v1.Address.TcpIpAddress;
42 import io.grpc.channelz.v1.Address.UdsAddress;
43 import io.grpc.channelz.v1.Channel;
44 import io.grpc.channelz.v1.ChannelConnectivityState;
45 import io.grpc.channelz.v1.ChannelConnectivityState.State;
46 import io.grpc.channelz.v1.ChannelData;
47 import io.grpc.channelz.v1.ChannelRef;
48 import io.grpc.channelz.v1.ChannelTrace;
49 import io.grpc.channelz.v1.ChannelTraceEvent;
50 import io.grpc.channelz.v1.ChannelTraceEvent.Severity;
51 import io.grpc.channelz.v1.GetServerSocketsResponse;
52 import io.grpc.channelz.v1.GetServersResponse;
53 import io.grpc.channelz.v1.GetTopChannelsResponse;
54 import io.grpc.channelz.v1.Security;
55 import io.grpc.channelz.v1.Security.OtherSecurity;
56 import io.grpc.channelz.v1.Security.Tls;
57 import io.grpc.channelz.v1.Server;
58 import io.grpc.channelz.v1.ServerData;
59 import io.grpc.channelz.v1.ServerRef;
60 import io.grpc.channelz.v1.Socket;
61 import io.grpc.channelz.v1.SocketData;
62 import io.grpc.channelz.v1.SocketOption;
63 import io.grpc.channelz.v1.SocketOptionLinger;
64 import io.grpc.channelz.v1.SocketOptionTcpInfo;
65 import io.grpc.channelz.v1.SocketOptionTimeout;
66 import io.grpc.channelz.v1.SocketRef;
67 import io.grpc.channelz.v1.Subchannel;
68 import io.grpc.channelz.v1.SubchannelRef;
69 import java.net.InetSocketAddress;
70 import java.net.SocketAddress;
71 import java.security.cert.CertificateEncodingException;
72 import java.util.ArrayList;
73 import java.util.Collections;
74 import java.util.List;
75 import java.util.Map;
76 import java.util.concurrent.ExecutionException;
77 import java.util.logging.Level;
78 import java.util.logging.Logger;
79 
80 /**
81  * A static utility class for turning internal data structures into protos.
82  */
83 final class ChannelzProtoUtil {
84   private static final Logger logger = Logger.getLogger(ChannelzProtoUtil.class.getName());
85 
ChannelzProtoUtil()86   private ChannelzProtoUtil() {
87     // do not instantiate.
88   }
89 
toChannelRef(InternalWithLogId obj)90   static ChannelRef toChannelRef(InternalWithLogId obj) {
91     return ChannelRef
92         .newBuilder()
93         .setChannelId(obj.getLogId().getId())
94         .setName(obj.toString())
95         .build();
96   }
97 
toSubchannelRef(InternalWithLogId obj)98   static SubchannelRef toSubchannelRef(InternalWithLogId obj) {
99     return SubchannelRef
100         .newBuilder()
101         .setSubchannelId(obj.getLogId().getId())
102         .setName(obj.toString())
103         .build();
104   }
105 
toServerRef(InternalWithLogId obj)106   static ServerRef toServerRef(InternalWithLogId obj) {
107     return ServerRef
108         .newBuilder()
109         .setServerId(obj.getLogId().getId())
110         .setName(obj.toString())
111         .build();
112   }
113 
toSocketRef(InternalWithLogId obj)114   static SocketRef toSocketRef(InternalWithLogId obj) {
115     return SocketRef
116         .newBuilder()
117         .setSocketId(obj.getLogId().getId())
118         .setName(obj.toString())
119         .build();
120   }
121 
toServer(InternalInstrumented<ServerStats> obj)122   static Server toServer(InternalInstrumented<ServerStats> obj) {
123     ServerStats stats = getFuture(obj.getStats());
124     Server.Builder builder = Server
125         .newBuilder()
126         .setRef(toServerRef(obj))
127         .setData(toServerData(stats));
128     for (InternalInstrumented<SocketStats> listenSocket : stats.listenSockets) {
129       builder.addListenSocket(toSocketRef(listenSocket));
130     }
131     return builder.build();
132   }
133 
toServerData(ServerStats stats)134   static ServerData toServerData(ServerStats stats) {
135     return ServerData
136         .newBuilder()
137         .setCallsStarted(stats.callsStarted)
138         .setCallsSucceeded(stats.callsSucceeded)
139         .setCallsFailed(stats.callsFailed)
140         .setLastCallStartedTimestamp(Timestamps.fromNanos(stats.lastCallStartedNanos))
141         .build();
142   }
143 
toSecurity(InternalChannelz.Security security)144   static Security toSecurity(InternalChannelz.Security security) {
145     Preconditions.checkNotNull(security);
146     Preconditions.checkState(
147         security.tls != null ^ security.other != null,
148         "one of tls or othersecurity must be non null");
149     if (security.tls != null) {
150       Tls.Builder tlsBuilder
151           = Tls.newBuilder().setStandardName(security.tls.cipherSuiteStandardName);
152       try {
153         if (security.tls.localCert != null) {
154           tlsBuilder.setLocalCertificate(ByteString.copyFrom(
155               security.tls.localCert.getEncoded()));
156         }
157         if (security.tls.remoteCert != null) {
158           tlsBuilder.setRemoteCertificate(ByteString.copyFrom(
159               security.tls.remoteCert.getEncoded()));
160         }
161       } catch (CertificateEncodingException e) {
162         logger.log(Level.FINE, "Caught exception", e);
163       }
164       return Security.newBuilder().setTls(tlsBuilder).build();
165     } else {
166       OtherSecurity.Builder builder = OtherSecurity.newBuilder().setName(security.other.name);
167       if (security.other.any != null) {
168         builder.setValue((Any) security.other.any);
169       }
170       return Security.newBuilder().setOther(builder).build();
171     }
172   }
173 
toSocket(InternalInstrumented<SocketStats> obj)174   static Socket toSocket(InternalInstrumented<SocketStats> obj) {
175     SocketStats socketStats = getFuture(obj.getStats());
176     Socket.Builder builder = Socket.newBuilder()
177         .setRef(toSocketRef(obj))
178         .setLocal(toAddress(socketStats.local));
179     if (socketStats.security != null) {
180       builder.setSecurity(toSecurity(socketStats.security));
181     }
182     // listen sockets do not have remote nor data
183     if (socketStats.remote != null) {
184       builder.setRemote(toAddress(socketStats.remote));
185     }
186     builder.setData(extractSocketData(socketStats));
187     return builder.build();
188   }
189 
toAddress(SocketAddress address)190   static Address toAddress(SocketAddress address) {
191     Preconditions.checkNotNull(address);
192     Address.Builder builder = Address.newBuilder();
193     if (address instanceof InetSocketAddress) {
194       InetSocketAddress inetAddress = (InetSocketAddress) address;
195       builder.setTcpipAddress(
196           TcpIpAddress
197               .newBuilder()
198               .setIpAddress(
199                   ByteString.copyFrom(inetAddress.getAddress().getAddress()))
200               .setPort(inetAddress.getPort())
201               .build());
202     } else if (address.getClass().getName().endsWith("io.netty.channel.unix.DomainSocketAddress")) {
203       builder.setUdsAddress(
204           UdsAddress
205               .newBuilder()
206               .setFilename(address.toString()) // DomainSocketAddress.toString returns filename
207               .build());
208     } else {
209       builder.setOtherAddress(OtherAddress.newBuilder().setName(address.toString()).build());
210     }
211     return builder.build();
212   }
213 
extractSocketData(SocketStats socketStats)214   static SocketData extractSocketData(SocketStats socketStats) {
215     SocketData.Builder builder = SocketData.newBuilder();
216     if (socketStats.data != null) {
217       TransportStats s = socketStats.data;
218       builder
219           .setStreamsStarted(s.streamsStarted)
220           .setStreamsSucceeded(s.streamsSucceeded)
221           .setStreamsFailed(s.streamsFailed)
222           .setMessagesSent(s.messagesSent)
223           .setMessagesReceived(s.messagesReceived)
224           .setKeepAlivesSent(s.keepAlivesSent)
225           .setLastLocalStreamCreatedTimestamp(
226               Timestamps.fromNanos(s.lastLocalStreamCreatedTimeNanos))
227           .setLastRemoteStreamCreatedTimestamp(
228               Timestamps.fromNanos(s.lastRemoteStreamCreatedTimeNanos))
229           .setLastMessageSentTimestamp(
230               Timestamps.fromNanos(s.lastMessageSentTimeNanos))
231           .setLastMessageReceivedTimestamp(
232               Timestamps.fromNanos(s.lastMessageReceivedTimeNanos))
233           .setLocalFlowControlWindow(
234               Int64Value.of(s.localFlowControlWindow))
235           .setRemoteFlowControlWindow(
236               Int64Value.of(s.remoteFlowControlWindow));
237     }
238     builder.addAllOption(toSocketOptionsList(socketStats.socketOptions));
239     return builder.build();
240   }
241 
242   public static final String SO_LINGER = "SO_LINGER";
243   public static final String SO_TIMEOUT = "SO_TIMEOUT";
244   public static final String TCP_INFO = "TCP_INFO";
245 
toSocketOptionLinger(int lingerSeconds)246   static SocketOption toSocketOptionLinger(int lingerSeconds) {
247     final SocketOptionLinger lingerOpt;
248     if (lingerSeconds >= 0) {
249       lingerOpt = SocketOptionLinger
250           .newBuilder()
251           .setActive(true)
252           .setDuration(Durations.fromSeconds(lingerSeconds))
253           .build();
254     } else {
255       lingerOpt = SocketOptionLinger.getDefaultInstance();
256     }
257     return SocketOption
258         .newBuilder()
259         .setName(SO_LINGER)
260         .setAdditional(Any.pack(lingerOpt))
261         .build();
262   }
263 
toSocketOptionTimeout(String name, int timeoutMillis)264   static SocketOption toSocketOptionTimeout(String name, int timeoutMillis) {
265     Preconditions.checkNotNull(name);
266     return SocketOption
267         .newBuilder()
268         .setName(name)
269         .setAdditional(
270             Any.pack(
271                 SocketOptionTimeout
272                     .newBuilder()
273                     .setDuration(Durations.fromMillis(timeoutMillis))
274                     .build()))
275         .build();
276   }
277 
toSocketOptionTcpInfo(InternalChannelz.TcpInfo i)278   static SocketOption toSocketOptionTcpInfo(InternalChannelz.TcpInfo i) {
279     SocketOptionTcpInfo tcpInfo = SocketOptionTcpInfo.newBuilder()
280         .setTcpiState(i.state)
281         .setTcpiCaState(i.caState)
282         .setTcpiRetransmits(i.retransmits)
283         .setTcpiProbes(i.probes)
284         .setTcpiBackoff(i.backoff)
285         .setTcpiOptions(i.options)
286         .setTcpiSndWscale(i.sndWscale)
287         .setTcpiRcvWscale(i.rcvWscale)
288         .setTcpiRto(i.rto)
289         .setTcpiAto(i.ato)
290         .setTcpiSndMss(i.sndMss)
291         .setTcpiRcvMss(i.rcvMss)
292         .setTcpiUnacked(i.unacked)
293         .setTcpiSacked(i.sacked)
294         .setTcpiLost(i.lost)
295         .setTcpiRetrans(i.retrans)
296         .setTcpiFackets(i.fackets)
297         .setTcpiLastDataSent(i.lastDataSent)
298         .setTcpiLastAckSent(i.lastAckSent)
299         .setTcpiLastDataRecv(i.lastDataRecv)
300         .setTcpiLastAckRecv(i.lastAckRecv)
301         .setTcpiPmtu(i.pmtu)
302         .setTcpiRcvSsthresh(i.rcvSsthresh)
303         .setTcpiRtt(i.rtt)
304         .setTcpiRttvar(i.rttvar)
305         .setTcpiSndSsthresh(i.sndSsthresh)
306         .setTcpiSndCwnd(i.sndCwnd)
307         .setTcpiAdvmss(i.advmss)
308         .setTcpiReordering(i.reordering)
309         .build();
310     return SocketOption
311         .newBuilder()
312         .setName(TCP_INFO)
313         .setAdditional(Any.pack(tcpInfo))
314         .build();
315   }
316 
toSocketOptionAdditional(String name, String value)317   static SocketOption toSocketOptionAdditional(String name, String value) {
318     Preconditions.checkNotNull(name);
319     Preconditions.checkNotNull(value);
320     return SocketOption.newBuilder().setName(name).setValue(value).build();
321   }
322 
toSocketOptionsList(InternalChannelz.SocketOptions options)323   static List<SocketOption> toSocketOptionsList(InternalChannelz.SocketOptions options) {
324     Preconditions.checkNotNull(options);
325     List<SocketOption> ret = new ArrayList<>();
326     if (options.lingerSeconds != null) {
327       ret.add(toSocketOptionLinger(options.lingerSeconds));
328     }
329     if (options.soTimeoutMillis != null) {
330       ret.add(toSocketOptionTimeout(SO_TIMEOUT, options.soTimeoutMillis));
331     }
332     if (options.tcpInfo != null) {
333       ret.add(toSocketOptionTcpInfo(options.tcpInfo));
334     }
335     for (Map.Entry<String, String> entry : options.others.entrySet()) {
336       ret.add(toSocketOptionAdditional(entry.getKey(), entry.getValue()));
337     }
338     return ret;
339   }
340 
toChannel(InternalInstrumented<ChannelStats> channel)341   static Channel toChannel(InternalInstrumented<ChannelStats> channel) {
342     ChannelStats stats = getFuture(channel.getStats());
343     Channel.Builder channelBuilder = Channel
344         .newBuilder()
345         .setRef(toChannelRef(channel))
346         .setData(extractChannelData(stats));
347     for (InternalWithLogId subchannel : stats.subchannels) {
348       channelBuilder.addSubchannelRef(toSubchannelRef(subchannel));
349     }
350 
351     return channelBuilder.build();
352   }
353 
extractChannelData(InternalChannelz.ChannelStats stats)354   static ChannelData extractChannelData(InternalChannelz.ChannelStats stats) {
355     ChannelData.Builder builder = ChannelData.newBuilder();
356     builder.setTarget(stats.target)
357         .setState(toChannelConnectivityState(stats.state))
358         .setCallsStarted(stats.callsStarted)
359         .setCallsSucceeded(stats.callsSucceeded)
360         .setCallsFailed(stats.callsFailed)
361         .setLastCallStartedTimestamp(Timestamps.fromNanos(stats.lastCallStartedNanos));
362     if (stats.channelTrace != null) {
363       builder.setTrace(toChannelTrace(stats.channelTrace));
364     }
365     return builder.build();
366   }
367 
toChannelConnectivityState(ConnectivityState s)368   static ChannelConnectivityState toChannelConnectivityState(ConnectivityState s) {
369     return ChannelConnectivityState.newBuilder().setState(toState(s)).build();
370   }
371 
toChannelTrace(InternalChannelz.ChannelTrace channelTrace)372   private static ChannelTrace toChannelTrace(InternalChannelz.ChannelTrace channelTrace) {
373     return ChannelTrace.newBuilder()
374         .setNumEventsLogged(channelTrace.numEventsLogged)
375         .setCreationTimestamp(Timestamps.fromNanos(channelTrace.creationTimeNanos))
376         .addAllEvents(toChannelTraceEvents(channelTrace.events))
377         .build();
378   }
379 
toChannelTraceEvents(List<Event> events)380   private static List<ChannelTraceEvent> toChannelTraceEvents(List<Event> events) {
381     List<ChannelTraceEvent> channelTraceEvents = new ArrayList<>();
382     for (Event event : events) {
383       ChannelTraceEvent.Builder builder = ChannelTraceEvent.newBuilder()
384           .setDescription(event.description)
385           .setSeverity(Severity.valueOf(event.severity.name()))
386           .setTimestamp(Timestamps.fromNanos(event.timestampNanos));
387       if (event.channelRef != null) {
388         builder.setChannelRef(toChannelRef(event.channelRef));
389       }
390       if (event.subchannelRef != null) {
391         builder.setSubchannelRef(toSubchannelRef(event.subchannelRef));
392       }
393       channelTraceEvents.add(builder.build());
394     }
395     return Collections.unmodifiableList(channelTraceEvents);
396   }
397 
toState(ConnectivityState state)398   static State toState(ConnectivityState state) {
399     if (state == null) {
400       return State.UNKNOWN;
401     }
402     try {
403       return Enum.valueOf(State.class, state.name());
404     } catch (IllegalArgumentException e) {
405       return State.UNKNOWN;
406     }
407   }
408 
toSubchannel(InternalInstrumented<ChannelStats> subchannel)409   static Subchannel toSubchannel(InternalInstrumented<ChannelStats> subchannel) {
410     ChannelStats stats = getFuture(subchannel.getStats());
411     Subchannel.Builder subchannelBuilder = Subchannel
412         .newBuilder()
413         .setRef(toSubchannelRef(subchannel))
414         .setData(extractChannelData(stats));
415     Preconditions.checkState(stats.sockets.isEmpty() || stats.subchannels.isEmpty());
416     for (InternalWithLogId childSocket : stats.sockets) {
417       subchannelBuilder.addSocketRef(toSocketRef(childSocket));
418     }
419     for (InternalWithLogId childSubchannel : stats.subchannels) {
420       subchannelBuilder.addSubchannelRef(toSubchannelRef(childSubchannel));
421     }
422     return subchannelBuilder.build();
423   }
424 
toGetTopChannelResponse(RootChannelList rootChannels)425   static GetTopChannelsResponse toGetTopChannelResponse(RootChannelList rootChannels) {
426     GetTopChannelsResponse.Builder responseBuilder = GetTopChannelsResponse
427         .newBuilder()
428         .setEnd(rootChannels.end);
429     for (InternalInstrumented<ChannelStats> c : rootChannels.channels) {
430       responseBuilder.addChannel(ChannelzProtoUtil.toChannel(c));
431     }
432     return responseBuilder.build();
433   }
434 
toGetServersResponse(ServerList servers)435   static GetServersResponse toGetServersResponse(ServerList servers) {
436     GetServersResponse.Builder responseBuilder = GetServersResponse
437         .newBuilder()
438         .setEnd(servers.end);
439     for (InternalInstrumented<ServerStats> s : servers.servers) {
440       responseBuilder.addServer(ChannelzProtoUtil.toServer(s));
441     }
442     return responseBuilder.build();
443   }
444 
toGetServerSocketsResponse(ServerSocketsList serverSockets)445   static GetServerSocketsResponse toGetServerSocketsResponse(ServerSocketsList serverSockets) {
446     GetServerSocketsResponse.Builder responseBuilder = GetServerSocketsResponse
447         .newBuilder()
448         .setEnd(serverSockets.end);
449     for (InternalWithLogId s : serverSockets.sockets) {
450       responseBuilder.addSocketRef(ChannelzProtoUtil.toSocketRef(s));
451     }
452     return responseBuilder.build();
453   }
454 
getFuture(ListenableFuture<T> future)455   private static <T> T getFuture(ListenableFuture<T> future) {
456     try {
457       T ret = future.get();
458       if (ret == null) {
459         throw Status.UNIMPLEMENTED
460             .withDescription("The entity's stats can not be retrieved. "
461                 + "If this is an InProcessTransport this is expected.")
462             .asRuntimeException();
463       }
464       return ret;
465     } catch (InterruptedException e) {
466       throw Status.INTERNAL.withCause(e).asRuntimeException();
467     } catch (ExecutionException e) {
468       throw Status.INTERNAL.withCause(e).asRuntimeException();
469     }
470   }
471 }
472