Source code for insights.parsers.sctp

"""
SCTP Socket State Parser
========================

Parsers provided by this module include:

SCTPEps - file ``/proc/net/sctp/eps``
-------------------------------------

SCTPAsc - file ``/proc/net/sctp/assocs`` on RHEL-6
---------------------------------------------------

SCTPAsc7 - file ``/proc/net/sctp/assocs`` on RHEL-7
---------------------------------------------------

SCTPSnmp - file ``/proc/net/sctp/snmp``
---------------------------------------
"""
from insights.components.rhel_version import IsRhel6, IsRhel7
from insights.core import Parser
from insights.core.exceptions import ParseException, SkipComponent
from insights.core.plugins import parser
from insights.parsers import keyword_search
from insights.specs import Specs


[docs] @parser(Specs.sctp_eps) class SCTPEps(Parser): """ This parser parses the content of ``/proc/net/sctp/eps`` file. It returns a list of dictionaries. The dictionary contains detail information of individual SCTP endpoint, which includes Endpoints, Socket, Socket type, Socket State, hash bucket, bind port, UID, socket inodes, Local IP address. Typical contents of ``/proc/net/sctp/eps`` file are:: ENDPT SOCK STY SST HBKT LPORT UID INODE LADDRS ffff88017e0a0200 ffff880300f7fa00 2 10 29 11165 200 299689357 10.0.0.102 10.0.0.70 ffff880612e81c00 ffff8803c28a1b00 2 10 30 11166 200 273361203 10.0.0.102 10.0.0.70 172.31.1.2 Output data is stored in the list of dictionaries Examples: >>> type(sctp_info) <class 'insights.parsers.sctp.SCTPEps'> >>> sorted(sctp_info.sctp_local_ports) == sorted(['11165', '11166']) True >>> sorted(sctp_info.sctp_local_ips) == sorted(['10.0.0.102', '10.0.0.70', '172.31.1.2']) True >>> sorted(sctp_info.search(local_port="11165")) == sorted([{'endpoints': 'ffff88017e0a0200', 'socket': 'ffff880299f7fa00', 'sk_type': '2', 'sk_state': '10', 'hash_bkt': '29', 'local_port': '11165', 'uid': '200', 'inode': '299689357', 'local_addr': ['10.0.0.102', '10.0.0.70']}]) True >>> len(sctp_info.search(local_port="11165")) == 1 True >>> len(sctp_info.search(endpoints="ffff88017e0a0200")) == 1 True >>> sctp_info.sctp_eps_ips {'ffff88017e0a0200': ['10.0.0.102', '10.0.0.70'], 'ffff880612e81c00': ['10.0.0.102', '10.0.0.70', '172.31.1.2']} """ COLUMN_IDX = [ 'endpoints', 'socket', 'sk_type', 'sk_state', 'hash_bkt', 'local_port', 'uid', 'inode', 'local_addr' ]
[docs] def parse_content(self, content): if (not content) or (not self.file_path): raise SkipComponent("No Contents") line = content[0].strip().split() keys_cnt = len(self.COLUMN_IDX) if "LPORT" not in line or len(line) != keys_cnt: raise ParseException("The following line is not compatible with this parser: {0}".format(line)) self.data = [] for line in content[1:]: line = line.strip().split(None, keys_cnt - 1) line[-1] = line[-1].split() self.data.append(dict(zip(self.COLUMN_IDX, line))) self._sctp_local_ports = set() self._sctp_local_ips = set() self._sctp_eps_ips = {} for line in self.data: self._sctp_local_ports.add(line['local_port']) local_addr = line['local_addr'] self._sctp_local_ips.update(local_addr) if line['endpoints'] not in self._sctp_eps_ips: self._sctp_eps_ips[line['endpoints']] = [] self._sctp_eps_ips[line['endpoints']].extend(local_addr)
@property def sctp_local_ports(self): """ (list): This function returns a list of SCTP ports if SCTP endpoints are created, else `[]`. """ return sorted(self._sctp_local_ports) @property def sctp_local_ips(self): """ (list): This function returns a list of all local ip addresses if SCTP endpoints are created, else `[]`. """ return sorted(self._sctp_local_ips) @property def sctp_eps_ips(self): """ (dict): This function returns a dict of all endpoints and corresponding local ip addresses used by SCTP endpoints if SCTP endpoints are created, else `{}`. """ return self._sctp_eps_ips
[docs] def search(self, **args): """ (list): This function return a list of all endpoints when args search matches, when args search do not match then it returns `[]`. """ return keyword_search(self.data, **args)
[docs] class SCTPAscBase(Parser): """ This parser parses the content of ``/proc/net/sctp/assocs`` file. And returns a list of dictionaries. The dictionary contains details of individual SCTP endpoint, which includes Association Struct, Socket, Socket type, Socket State, Association state, hash bucket, association id, tx queue, rx queue, uid, inode, local port, remote port, 'local addr, remote addr, heartbeat interval, max in-stream, max out-stream, max retransmission attempt, number of init chunks send, number of shutdown chunks send, data chunks retransmitted' """
[docs] def parse_content(self, content): self.data = [] self._sctp_local_ports = set() self._sctp_remote_ports = set() self._sctp_local_ips = set() self._sctp_remote_ips = set() if (not content) or (not self.file_path): raise SkipComponent("No Contents") line = content[0].strip().split() keys_cnt = len(self.COLUMN_IDX) if "LPORT" not in line or len(line) != keys_cnt: raise ParseException("The following line is not compatible with this parser: {0}".format(line)) laddr_idx = line.index('LADDRS') raddr_ridx = len(line) - line.index('RADDRS') for line in content[1:]: line_1 = line.strip().split(None, laddr_idx) line_end = line_1.pop() idx = line_end.index('<->') laddrs = line_end[:idx].strip().split() line_end = line_end[idx + 3:].strip().rsplit(None, raddr_ridx - 1) raddrs = line_end.pop(0).split() line_1.append(laddrs) line_1.append(raddrs) line_1.extend(line_end) self.data.append(dict(zip(self.COLUMN_IDX[:-1], line_1))) for line in self.data: self._sctp_local_ports.add(line['local_port']) self._sctp_remote_ports.add(line['remote_port']) self._sctp_local_ips.update(line['local_addr']) self._sctp_remote_ips.update(line['remote_addr'])
@property def sctp_local_ports(self): """ (list): This function returns a list of SCTP local peer ports if SCTP endpoints are created, else `[]`. """ return sorted(self._sctp_local_ports) @property def sctp_remote_ports(self): """ (list): This function returns a list of SCTP remote peer ports if SCTP endpoints are created, else `[]`. """ return sorted(self._sctp_remote_ports) @property def sctp_local_ips(self): """ (list): This function returns a list of all local peer's ip addresses if SCTP endpoints are created, else `[]`. """ return sorted(self._sctp_local_ips) @property def sctp_remote_ips(self): """ (list): This function returns a list of all remote peer's ip addresses if SCTP endpoints are created, else `[]`. """ return sorted(self._sctp_remote_ips)
[docs] def search(self, **args): """ (list): This function return a list of all SCTP associations when args search matches, when args search do not match then it returns `[]`. """ return keyword_search(self.data, **args)
[docs] @parser(Specs.sctp_asc, IsRhel6) class SCTPAsc(SCTPAscBase): """ This parser parses the file ``/proc/net/sctp/assocs`` from RHEL-6. It has different columns as compare to RHEL-7. Typical contents of ``/proc/net/sctp/assocs`` on RHEL-6 file are:: ASSOC SOCK STY SST ST HBKT ASSOC-ID TX_QUEUE RX_QUEUE UID INODE LPORT RPORT LADDRS <-> RADDRS HBINT INS OUTS MAXRT T1X T2X RTXC ffff88045ac7e000 ffff88062077aa00 2 1 4 1205 963 0 0 200 273361167 11567 11166 10.0.0.102 10.0.0.70 <-> *10.0.0.109 10.0.0.77 1000 2 2 10 0 0 0 ffff88061fbf2000 ffff88060ff92500 2 1 4 1460 942 0 0 200 273360669 11566 11167 10.0.0.102 10.0.0.70 <-> *10.0.0.109 10.0.0.77 1000 2 2 10 0 0 0 Output data is stored in the list of dictionaries Examples: >>> type(sctp_asc) <class 'insights.parsers.sctp.SCTPAsc'> >>> sorted(sctp_asc.sctp_local_ports) == sorted(['11567','11566']) True >>> sorted(sctp_asc.sctp_remote_ports) == sorted(['11166','11167']) True >>> sorted(sctp_asc.sctp_local_ips) == sorted(['10.0.0.102', '10.0.0.70']) True >>> sorted(sctp_asc.sctp_remote_ips) == sorted(['*10.0.0.109', '10.0.0.77']) True >>> sorted(sctp_asc.search(local_port='11566')) == sorted([{'init_chunks_send': '0', 'uid': '200', 'shutdown_chunks_send': '0', 'max_outstream': '2', 'tx_que': '0', 'inode': '273360669', 'hrtbt_intrvl': '1000', 'sk_type': '2', 'remote_addr': ['*10.0.0.109', '10.0.0.77'], 'data_chunks_retrans': '0', 'local_addr': ['10.0.0.102', '10.0.0.70'], 'asc_id': '942', 'max_instream': '2', 'remote_port': '11167', 'asc_state': '4', 'max_retrans_atmpt': '10', 'sk_state': '1', 'socket': 'ffff88060ff92500', 'asc_struct': 'ffff88061fbf2000', 'local_port': '11566', 'hash_bkt': '1460', 'rx_que': '0'}]) True """ def __init__(self, *args, **kwargs): self.COLUMN_IDX = [ 'asc_struct', 'socket', 'sk_type', 'sk_state', 'asc_state', 'hash_bkt', 'asc_id', 'tx_que', 'rx_que', 'uid', 'inode', 'local_port', 'remote_port', 'local_addr', 'remote_addr', 'hrtbt_intrvl', 'max_instream', 'max_outstream', 'max_retrans_atmpt', 'init_chunks_send', 'shutdown_chunks_send', 'data_chunks_retrans', 'relation', # should be ignore ] super(SCTPAsc, self).__init__(*args, **kwargs)
[docs] @parser(Specs.sctp_asc, IsRhel7) class SCTPAsc7(SCTPAscBase): """ This parser parses the file ``/proc/net/sctp/assocs`` from RHEL-7. It has different columns as compare to RHEL-6. Typical contents of ``/proc/net/sctp/assocs`` on RHEL-7 file are:: ASSOC SOCK STY SST ST HBKT ASSOC-ID TX_QUEUE RX_QUEUE UID INODE LPORT RPORT LADDRS <-> RADDRS HBINT INS OUTS MAXRT T1X T2X RTXC wmema wmemq sndbuf rcvbuf ffff8805d36b3000 ffff880f8911f380 0 10 3 0 12754 0 0 0 496595 3868 3868 10.131.222.5 <-> *10.131.160.81 10.131.176.81 30000 17 10 10 0 0 0 11 12 1000000 2000000 ffff8805f17e1000 ffff881004aff380 0 10 3 0 12728 0 0 0 532396 3868 3868 10.131.222.3 <-> *10.131.160.81 10.131.176.81 30000 17 10 10 0 0 0 13 14 3000000 4000000 Output data is stored in the list of dictionaries Examples: >>> type(sctp_asc_7) <class 'insights.parsers.sctp.SCTPAsc7'> >>> sctp_asc_7.sctp_local_ips == sorted(['10.131.222.5', '10.131.222.3']) True >>> sctp_asc_7.data[0]['rcvbuf'] '2000000' """ def __init__(self, *args, **kwargs): self.COLUMN_IDX = [ 'asc_struct', 'socket', 'sk_type', 'sk_state', 'asc_state', 'hash_bkt', 'asc_id', 'tx_que', 'rx_que', 'uid', 'inode', 'local_port', 'remote_port', 'local_addr', 'remote_addr', 'hrtbt_intrvl', 'max_instream', 'max_outstream', 'max_retrans_atmpt', 'init_chunks_send', 'shutdown_chunks_send', 'data_chunks_retrans', 'wmema', 'wmemq', 'sndbuf', 'rcvbuf', 'relation', # should be ignore ] super(SCTPAsc7, self).__init__(*args, **kwargs)
[docs] @parser(Specs.sctp_snmp) class SCTPSnmp(Parser, dict): """ This parser parses the content of ``/proc/net/sctp/snmp`` file, which contains statistics related to SCTP states, packets and chunks. Sample content:: SctpCurrEstab 5380 SctpActiveEstabs 12749 SctpPassiveEstabs 55 SctpAborteds 2142 SctpShutdowns 5295 SctpOutOfBlues 36786 SctpChecksumErrors 0 SctpOutCtrlChunks 1051492 Data is stored in a dictionary. Examples: >>> type(sctp_snmp) <class 'insights.parsers.sctp.SCTPSnmp'> >>> sctp_snmp.get('SctpCurrEstab') 5380 >>> sctp_snmp.get('SctpChecksumErrors') == 0 True >>> 'SctpShutdowns' in sctp_snmp True >>> len(sctp_snmp) 8 >>> sorted(sctp_snmp.keys()) ['SctpAborteds', 'SctpActiveEstabs', 'SctpChecksumErrors', 'SctpCurrEstab', 'SctpOutCtrlChunks', 'SctpOutOfBlues', 'SctpPassiveEstabs', 'SctpShutdowns'] Resultant Data:: { 'SctpCurrEstab': 5380, 'SctpActiveEstabs': 12749, 'SctpPassiveEstabs': 55, 'SctpAborteds': 2142, 'SctpShutdowns': 5295, 'SctpOutOfBlues': 36786, 'SctpChecksumErrors': 0, ... ... } Raises: SkipComponent: When contents are empty. ParseException: When file contents are not in expected format. """
[docs] def parse_content(self, content): if (not content) or (not self.file_path): raise SkipComponent("No Contents") for line in content: line_strip = line.split() if len(line_strip) != 2: raise ParseException("Contents are not compatible to this parser") self[line_strip[0]] = int(line_strip[1])