#!/usr/bin/env pmpython # # Copyright (c) 2023 Oracle and/or its affiliates. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # pylint: disable=bad-whitespace,too-many-arguments,too-many-lines, bad-continuation, line-too-long # pylint: disable=redefined-outer-name,unnecessary-lambda, wildcard-import, unused-wildcard-import # import sys import time from pcp import pmapi from pcp import pmcc from collections import OrderedDict from cpmapi import PM_CONTEXT_ARCHIVE IFF_UP = 1<<0 IFF_BROADCAST = 1<<1 IFF_DEBUG = 1<<2 IFF_LOOPBACK = 1<<3 IFF_POINTOPOINT = 1<<4 IFF_NOTRAILERS = 1<<5 IFF_RUNNING = 1<<6 IFF_NOARP = 1<<7 IFF_PROMISC = 1<<8 IFF_ALLMULTI = 1<<9 IFF_MASTER = 1<<10 IFF_SLAVE = 1<<11 IFF_MULTICAST = 1<<12 IFF_PORTSEL = 1<<13 IFF_AUTOMEDIA = 1<<14 IFF_DYNAMIC = 1<<15 IFF_LOWER_UP = 1<<16 IFF_DORMANT = 1<<17 IFF_ECHO = 1<<18 TCP_METRICS = ["network.tcp.activeopens", "network.tcp.passiveopens", "network.tcp.attemptfails", "network.tcp.estabresets", "network.tcpconn.established", "network.tcp.insegs", "network.tcp.outsegs", "network.tcp.retranssegs", "network.tcp.inerrs", "network.tcp.outrsts"] TCP_METRICS_DESC = [ "__ active connections openings", "__ passive connection openings", "__ failed connection attempts", "__ connection resets received", "__ connections established", "__ segments received", "__ segments send out", "__ segments retransmited", "__ bad segments received.", "__ resets sent"] TCP_EXT_METRICS = [ # tcpsock.finishedtimewait.fast_timer, "network.tcp.delayedacks", "network.tcp.delayedacklocked", # Quick ack mode was activated __ times, "network.tcp.hphits", # acknowledgments not containing data payload received, "network.tcp.hpacks", "network.tcp.sackrecovery", "network.tcp.sackreorder", "network.tcp.dsackundo", # congestion windows recovered without slow start after partial ack, "network.tcp.lostretransmit", "network.tcp.sackfailures", "network.tcp.fastretrans", "network.tcp.timeouts", "network.tcp.lossprobes", "network.tcp.lossproberecovery", # "TCPDSACKRecv", "network.tcp.tcpbacklogcoalesce", "network.tcp.dsackoldsent", "network.tcp.dsackofosent", "network.tcp.dsackrecv", "network.tcp.dsackoforecv", "network.tcp.abortondata", "network.tcp.abortonclose", "network.tcp.abortontimeout", "network.tcp.dsackignorednoundo", # "TCPSpuriousRTOs", "network.tcp.sackshifted", "network.tcp.sackmerged", "network.tcp.sackshiftfallback", "network.tcp.iprpfilter", "network.tcp.rcvcoalesce", "network.tcp.ofoqueue", "network.tcp.ofomerge", "network.tcp.challengeack", "network.tcp.synchallenge", "network.tcp.spuriousrtxhostqueues", "network.tcp.autocorking", # "TCPFromZeroWindowAdv: __", # "TCPToZeroWindowAdv: __", # "TCPWantZeroWindowAdv: __", "network.tcp.synretrans", "network.tcp.origdatasent", "network.tcp.tcphystarttraindetect", "network.tcp.tcphystarttraincwnd", "network.tcp.tcphystartdelaydetect", "network.tcp.tcphystartdelaycwnd", "network.tcp.tcpackskippedseq", "network.tcp.tcpkeepalive", "network.tcp.tcpdelivered", "network.tcp.tcpackcompressed"] TCP_EXT_METRICS_DESC = [ # "__ TCP sockets finished time wait in fast timer", "__ delayed acks sent", "__ delayed acks further delayed because of locked socket", # "Quick ack mode was activated __ times" , "__ packet headers predicted", # "__ acknowledgments not containing data payload received", "__ predicted acknowledgments", "TCPSackRecovery: __", "Detected reordering __ times using SACK", "TCPDSACKUndo: __", # "__ congestion windows recovered without slow start after partial ack", "TCPLostRetransmitt: __", "TCPSackFailures: __", "__ fast retransmits", "TCPTimeouts: __", "TCPLossProbes: __", "TCPLossProbeRecovery: __", # "TCPSackRecoveryFail: __" "TCPBacklogCoalesce: __", "TCPDSACKOldSent: __", "TCPDSACKOfoSent: __", "TCPDSACKRecv: __", "TCPDSACKOfoRecv: __", "__ connections aborted due to data", "__ connections reset due to early user close", "__ connections aborted due to timeout", "TCPDSACKIgnoredNoUndo: __", # "TCPSpuriousRTOs: __", "TCPSackShifted: __", "TCPSackMerged: __", "TCPSackShiftFallback: __", "IPReversePathFilter: __", "TCPRcvCoalesce: __", "TCPOFOQueue: __", "TCPOFOMerge: __", "TCPChallengeAck: __", "TCPSynChallenge: __", "TCPSpuriousRtxHostQueues: __", "TCPAutoCorking: __", # "TCPFromZeroWindowAdv: __", # "TCPToZeroWindowAdv: __", # "TCPWantZeroWindowAdv: __", "TCPSynRetrans: __", "TCPOrigDataSent: __", "TCPHystartTrainDetect: __", "TCPHystartTrainCwnd: __", "TCPHystartDelayDetect: __", "TCPHystartDelayCwnd: __", "TCPACKSkippedSeq: __", "TCPKeepAlive: __", "TCPDelivered: __", "TCPAckCompressed: __"] IP_METRICS = [ "network.ip.forwarding", "network.ip.inreceives", "network.ip.inaddrerrors", "network.ip.forwdatagrams", "network.ip.indiscards", "network.ip.indelivers", "network.ip.outrequests", "network.ip.outnoroutes"] IP_METRICS_DESC = [ "Forwarding: __", "__ total packets received", "__ with invalid addresses", "__ forwarded", "__ incoming packets discarded", "__ incoming packets delivered", "__ requests sent out", "__ dropped because of missing route"] IP_EXT_METRICS = [ "network.ip.inmcastpkts", "network.ip.inbcastpkts", "network.ip.inoctets", "network.ip.outoctets", "network.ip.inmcastoctets", "network.ip.inbcastoctets", "network.ip.noectpkts"] IP_EXT_METRICS_DESC = [ "InMcastPkts: __", "InBcastPkts: __", "InOctets: __", "OutOctets: __", "InMcastOctets: __", "InBcastOctets: __", "InNoECTpkts: __"] ICMP_METRICS = [ "network.icmp.inmsgs", "network.icmp.inerrors", "network.icmp.indestunreachs", # "echo requests", "network.icmp.outmsgs", "network.icmp.outerrors", "network.icmp.outdestunreachs"] # "echo replies"] ICMP_METRICS_DESC = [ "__ ICMP messages received", "__ Input ICMP message failed", "ICMP input histogram:\n\t\tdestination unreachable: __", # "\techo requests", "__ ICMP messages sent", "__ ICMP messages failed", "ICMP input histogram:\n\t\tOutput destination unreachable: __"] # "\techo replies"] ICMP_MSG_METRICS = [ "network.icmpmsg.intype", # "network.icmpmsg.intype8", "network.icmpmsg.outtype"] # "network.icmpmsg.outtype3"] ICMP_MSG_METRICS_DESC = [ "\tInType3: __", # "\tInType8: __", "\tOutType0: __"] # "\tOutType3: __"] UDP_METRICS = [ "network.udp.indatagrams", "network.udp.noports", "network.udp.inerrors", "network.udp.outdatagrams", "network.udp.recvbuferrors", "network.udp.sndbuferrors"] # "network.udp.ignoredmulti"] UDP_METRICS_DESC = [ "__ packets received", "__ packets to unknown port received", "__ packet receive errors", "__ packets sent", "__ receive buffer errors", "__ send buffer errors"] #"IgnoredMulti: __"] UDP_LITE_METRICS = [ "network.udplite.indatagrams", "network.udplite.noports", "network.udplite.inerrors", "network.udplite.outdatagrams", "network.udplite.recvbuferrors", "network.udplite.sndbuferrors", "network.udplite.incsumerrors"] UDP_LITE_METRICS_DESC = [ "__ packets received", "__ packets to unknown port received", "__ packet receive errors", "__ packets sent", "__ receive buffer errors", "__ send buffer errors", "__ checksum errors"] IFACE_METRICS = [ "network.interface.mtu", "network.interface.in.packets", "network.interface.in.errors", "network.interface.in.drops", # "ovr __ (override)", "network.interface.out.packets", "network.interface.out.errors", "network.interface.out.drops"] # "ovr __ (override)", # "network.interface.type"] # "network.interface.running", # "network.interface.up"] SYS_METRICS = ["kernel.uname.sysname", "kernel.uname.release", "kernel.uname.nodename", "kernel.uname.machine", "hinv.ncpu"] METRICS = IP_METRICS + ICMP_METRICS + ICMP_MSG_METRICS + TCP_METRICS + UDP_METRICS + UDP_LITE_METRICS + TCP_EXT_METRICS + IP_EXT_METRICS METRICS_DESC = IP_METRICS_DESC + ICMP_METRICS_DESC + ICMP_MSG_METRICS_DESC + TCP_METRICS_DESC + UDP_METRICS_DESC + UDP_LITE_METRICS_DESC + TCP_EXT_METRICS_DESC + IP_EXT_METRICS_DESC METRICS_DICT = OrderedDict() METRICS_DICT["Ip_METRICS"] = [IP_METRICS, IP_METRICS_DESC] METRICS_DICT["Icmp_METRICS"] = [ICMP_METRICS, ICMP_METRICS_DESC] METRICS_DICT["IcmpMsg_METRICS"] = [ICMP_MSG_METRICS, ICMP_MSG_METRICS_DESC] METRICS_DICT["Tcp_METRICS"] = [TCP_METRICS, TCP_METRICS_DESC] METRICS_DICT["Udp_METRICS"] = [UDP_METRICS, UDP_METRICS_DESC] METRICS_DICT["UdpLite_METRICS"] = [UDP_LITE_METRICS, UDP_LITE_METRICS_DESC] METRICS_DICT["TcpExt_METRICS"] = [TCP_EXT_METRICS, TCP_EXT_METRICS_DESC] METRICS_DICT["IpExt_METRICS"] = [IP_EXT_METRICS, IP_EXT_METRICS_DESC] MISSING_METRICS = [] class NetstatReport(pmcc.MetricGroupPrinter): def __init__(self, opts): self.opts = opts self.Machine_info_count = 0 def __get_ncpu(self, group): return group['hinv.ncpu'].netValues[0][2] def __print_machine_info(self, group, context): timestamp = context.pmLocaltime(group.timestamp.tv_sec) # Please check strftime(3) for different formatting options. # Also check TZ and LC_TIME environment variables for more # information on how to override the default formatting of # the date display in the header time_string = time.strftime("%x", timestamp.struct_time()) header_string = '' header_string += group['kernel.uname.sysname'].netValues[0][2] + ' ' header_string += group['kernel.uname.release'].netValues[0][2] + ' ' header_string += '(' + group['kernel.uname.nodename'].netValues[0][2] + ') ' header_string += time_string + ' ' header_string += group['kernel.uname.machine'].netValues[0][2] + ' ' print("%s (%s CPU)" % (header_string, self.__get_ncpu(group))) def print_metric(self, group, metrics,metrics_desc): idx = 0 for metric in metrics: if metric in MISSING_METRICS: metric_val_str = metrics_desc[idx].replace("__", "NA") print("\t%s" % (metric_val_str)) idx += 1 continue try: val = group[metric].netValues[0][2] except IndexError: metric_val_str = metrics_desc[idx].replace("__", "NA") print("\t%s" % (metric_val_str)) idx += 1 continue metric_val_str = metrics_desc[idx].replace("__", str(val)) # metric_val_str = metric_val_str print("\t%s" % (metric_val_str)) idx += 1 print("\n") def report(self, manager): group = manager["sysinfo"] try: if not self.Machine_info_count: self.__print_machine_info(group, manager) self.Machine_info_count = 1 except IndexError: # missing some metrics return group = manager["netstat"] self.opts.pmGetOptionSamples() t_s = group.contextCache.pmLocaltime(int(group.timestamp)) time_string = time.strftime(NetstatOptions.timefmt, t_s.struct_time()) print("%s\n" % time_string) if NetstatOptions.all_stats_flag is False and NetstatOptions.filter_protocol_flag is False and NetstatOptions.filter_iface_flag is False: NetstatOptions.all_stats_flag = True NetstatOptions.filter_iface_flag = True if NetstatOptions.all_stats_flag: for metric_dict in METRICS_DICT.items(): print("%s:" % metric_dict[0][:-8]) self.print_metric(group, metric_dict[1][0], metric_dict[1][1]) if NetstatOptions.filter_protocol_flag: protocol = NetstatOptions.filter_protocol for metric_dict in METRICS_DICT.items(): if protocol.lower() in metric_dict[0].lower(): print("%s:" % metric_dict[0][:-8]) self.print_metric(group, metric_dict[1][0], metric_dict[1][1]) if NetstatOptions.filter_iface_flag: ifstats = {} print("Kernel Interface table") for metric in IFACE_METRICS: idx = 0 try: val = group[metric].netValues except IndexError: idx += 1 continue for elem in val: if elem[1] not in ifstats: ifstats[elem[1]] = [] ifstats[elem[1]].append(str(elem[2])) print("%10s %10s %10s %10s %10s %10s %10s %10s"%("Iface", "MTU", "RX-OK", "RX-ERR", "RX-DRP", "TX-OK", "TX-ERR", "TX-DRP")) ifstats_str = "" for intf in sorted(ifstats): if_row = "" if_row += "%10s " % intf # idx = 0 for idx in range(len(ifstats[intf])): ele = ifstats[intf][idx] if_row += "%10s " % str(ele) ifstats_str += if_row + "\n" print(ifstats_str) if NetstatOptions.context is not PM_CONTEXT_ARCHIVE and self.opts.pmGetOptionSamples() is None: sys.exit(0) class NetstatOptions(pmapi.pmOptions): context = None timefmt = "%H:%M:%S" all_stats_flag = False filter_protocol_flag = False filter_protocol = None filter_iface_flag = False def override(self, opt): if opt == 'p': return 1 return 0 def extraOptions(self, opt, optarg, index): if opt == "statistics": NetstatOptions.all_stats_flag = True elif opt == 'p': NetstatOptions.filter_protocol_flag = True try: if optarg is not None: if optarg in ["TCP", "IP", "UDP", "ICMP"]: NetstatOptions.filter_protocol = str(optarg) except ValueError: print("Invalid command Id List: use comma separated pids without whitespaces") sys.exit(1) elif opt == 'i': NetstatOptions.filter_iface_flag = True def __init__(self): pmapi.pmOptions.__init__(self, "a:i?:s:S:T:p:z:") self.pmSetLongOptionStart() self.pmSetLongOptionFinish() self.pmSetLongOption("statistics", 0, "", "","shows output similar to netstat -s, which displays summary statistics for each protocol") self.pmSetLongOption("", 0, "i", "","shows output similar to netstat -i -a -n, which displays a table of all network interfaces") self.pmSetLongOption("", 1, "p", "[TCP|IP|UDP|ICMP]","filter stats specific to a protocol") self.pmSetOptionCallback(self.extraOptions) self.pmSetOverrideCallback(self.override) self.pmSetLongOptionHelp() if __name__ == '__main__': try: opts = NetstatOptions() mngr = pmcc.MetricGroupManager.builder(opts,sys.argv) NetstatOptions.context = mngr.type missing = mngr.checkMissingMetrics(METRICS) if missing is not None: for missing_metric in missing: METRICS.remove(missing_metric) MISSING_METRICS.append(missing_metric) mngr["netstat"] = METRICS + IFACE_METRICS mngr["sysinfo"] = SYS_METRICS mngr.printer = NetstatReport(opts) sts = mngr.run() sys.exit(sts) except pmapi.pmErr as error: sys.stderr.write("%s %s\n"%(error.progname(),error.message())) except pmapi.pmUsageErr as usage: usage.message() sys.exit(1) except KeyboardInterrupt: pass