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