diff --git a/jc/parsers/ifconfig.py b/jc/parsers/ifconfig.py index 60515a76..d724482a 100644 --- a/jc/parsers/ifconfig.py +++ b/jc/parsers/ifconfig.py @@ -141,16 +141,17 @@ Examples: } ] """ +import re +from collections import namedtuple import jc.utils -from ifconfigparser import IfconfigParser class info(): - version = '1.6' + version = '1.7' description = 'ifconfig command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' - details = 'Using ifconfig-parser package from https://github.com/KnightWhoSayNi/ifconfig-parser' + details = 'Using ifconfig-parser from https://github.com/KnightWhoSayNi/ifconfig-parser' # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux', 'aix', 'freebsd', 'darwin'] @@ -160,6 +161,200 @@ class info(): __version__ = info.version +class IfconfigParser(object): + # Author: threeheadedknight@protonmail.com + # Date created: 30.06.2018 17:03 + # Python Version: 3.7 + + attributes = ['name', 'type', 'mac_addr', 'ipv4_addr', 'ipv4_bcast', 'ipv4_mask', 'ipv6_addr', 'ipv6_mask', + 'ipv6_scope', 'state', 'mtu', 'metric', 'rx_packets', 'rx_errors', 'rx_dropped', 'rx_overruns', + 'rx_frame', 'tx_packets', 'tx_errors', 'tx_dropped', 'tx_overruns', 'tx_carrier', 'tx_collisions', + 'rx_bytes', 'tx_bytes'] + + def __init__(self, console_output): + """ + :param console_output: + """ + + if isinstance(console_output, list): + source_data = " ".join(console_output) + else: + source_data = console_output.replace("\n", " ") + self.interfaces = self.parser(source_data=source_data) + + def list_interfaces(self): + """ + :return: + """ + return sorted(self.interfaces.keys()) + + def count_interfaces(self): + """ + :return: + """ + return len(self.interfaces.keys()) + + def filter_interfaces(self, **kwargs): + """ + :param kwargs: + :return: + """ + for attr in kwargs.keys(): + if attr not in IfconfigParser.attributes: + raise ValueError("Attribute [{}] not supported.".format(attr)) + + filtered_interfaces = [] + for name, details in self.interfaces.items(): + + if all(getattr(details, attr) == kwargs[attr] for attr in kwargs.keys()): + filtered_interfaces.append(name) + + return sorted(filtered_interfaces) + + def get_interface(self, name): + """ + :param name: + :return: + """ + if name in self.list_interfaces(): + return self.interfaces[name] + else: + raise InterfaceNotFound("Interface [{}] not found.".format(name)) + + def get_interfaces(self): + """ + :return: + """ + return self.interfaces + + def is_available(self, name): + """ + :param name: + :return: + """ + return name in self.interfaces + + def parser(self, source_data): + """ + :param source_data: + :return: + """ + + # Linux syntax + re_linux_interface = re.compile( + r"(?P[a-zA-Z0-9:._-]+)\s+Link encap:(?P\S+\s?\S+)(\s+HWaddr\s+\b" + r"(?P[0-9A-Fa-f:?]+))?", + re.I) + re_linux_ipv4 = re.compile( + r"inet addr:(?P(?:[0-9]{1,3}\.){3}[0-9]{1,3})(\s+Bcast:" + r"(?P(?:[0-9]{1,3}\.){3}[0-9]{1,3}))?\s+Mask:(?P(?:[0-9]{1,3}\.){3}[0-9]{1,3})", + re.I) + re_linux_ipv6 = re.compile( + r"inet6 addr:\s+(?P\S+)/(?P[0-9]+)\s+Scope:(?PLink|Host)", + re.I) + re_linux_state = re.compile( + r"\W+(?P(?:\w+\s)+)(?:\s+)?MTU:(?P[0-9]+)\s+Metric:(?P[0-9]+)", re.I) + re_linux_rx = re.compile( + r"RX packets:(?P[0-9]+)\s+errors:(?P[0-9]+)\s+dropped:" + r"(?P[0-9]+)\s+overruns:(?P[0-9]+)\s+frame:(?P[0-9]+)", + re.I) + re_linux_tx = re.compile( + r"TX packets:(?P[0-9]+)\s+errors:(?P[0-9]+)\s+dropped:" + r"(?P[0-9]+)\s+overruns:(?P[0-9]+)\s+carrier:(?P[0-9]+)", + re.I) + re_linux_bytes = re.compile(r"\W+RX bytes:(?P\d+)\s+\(.*\)\s+TX bytes:(?P\d+)\s+\(.*\)", re.I) + re_linux_tx_stats = re.compile(r"collisions:(?P[0-9]+)\s+txqueuelen:[0-9]+", re.I) + re_linux = [re_linux_interface, re_linux_ipv4, re_linux_ipv6, re_linux_state, re_linux_rx, re_linux_tx, + re_linux_bytes, re_linux_tx_stats] + + # OpenBSD syntax + re_openbsd_interface = re.compile( + r"(?P[a-zA-Z0-9:._-]+):\s+flags=(?P[0-9]+)<(?P\S+)?>\s+mtu\s+(?P[0-9]+)", + re.I) + re_openbsd_ipv4 = re.compile( + r"inet (?P(?:[0-9]{1,3}\.){3}[0-9]{1,3})\s+netmask\s+" + r"(?P(?:[0-9]{1,3}\.){3}[0-9]{1,3})(\s+broadcast\s+" + r"(?P(?:[0-9]{1,3}\.){3}[0-9]{1,3}))?", + re.I) + re_openbsd_ipv6 = re.compile( + r"inet6\s+(?P\S+)\s+prefixlen\s+(?P[0-9]+)\s+scopeid\s+(?P\w+x\w+)<" + r"(?:link|host)>", + re.I) + re_openbsd_details = re.compile( + r"\S+\s+(?:(?P[0-9A-Fa-f:?]+)\s+)?txqueuelen\s+[0-9]+\s+\((?P\S+\s?\S+)\)", re.I) + re_openbsd_rx = re.compile(r"RX packets (?P[0-9]+)\s+bytes\s+(?P\d+)\s+.*", re.I) + re_openbsd_rx_stats = re.compile( + r"RX errors (?P[0-9]+)\s+dropped\s+(?P[0-9]+)\s+overruns\s+" + r"(?P[0-9]+)\s+frame\s+(?P[0-9]+)", + re.I) + re_openbsd_tx = re.compile(r"TX packets (?P[0-9]+)\s+bytes\s+(?P\d+)\s+.*", re.I) + re_openbsd_tx_stats = re.compile( + r"TX errors (?P[0-9]+)\s+dropped\s+(?P[0-9]+)\s+overruns\s+" + r"(?P[0-9]+)\s+carrier\s+(?P[0-9]+)\s+collisions\s+(?P[0-9]+)", + re.I) + re_openbsd = [re_openbsd_interface, re_openbsd_ipv4, re_openbsd_ipv6, re_openbsd_details, re_openbsd_rx, + re_openbsd_rx_stats, re_openbsd_tx, re_openbsd_tx_stats] + + # FreeBSD syntax + re_freebsd_interface = re.compile( + r"(?P[a-zA-Z0-9:._-]+):\s+flags=(?P[0-9]+)<(?P\S+)>\s+metric\s+" + r"(?P[0-9]+)\s+mtu\s+(?P[0-9]+)", + re.I) + re_freebsd_ipv4 = re.compile( + r"inet (?P(?:[0-9]{1,3}\.){3}[0-9]{1,3})\s+netmask\s+(?P0x\S+)(\s+broadcast\s+" + r"(?P(?:[0-9]{1,3}\.){3}[0-9]{1,3}))?", + re.I) + re_freebsd_ipv6 = re.compile(r"\s?inet6\s(?P.*)(?:\%\w+\d+)\sprefixlen\s(?P\d+)(?:\s\w+)?\sscopeid\s(?P\w+x\w+)", re.I) + re_freebsd_details = re.compile(r"ether\s+(?P[0-9A-Fa-f:?]+)", re.I) + re_freebsd = [re_freebsd_interface, re_freebsd_ipv4, re_freebsd_ipv6, re_freebsd_details] + + available_interfaces = dict() + + for pattern in [re_linux_interface, re_openbsd_interface, re_freebsd_interface]: + network_interfaces = re.finditer(pattern, source_data) + positions = [] + while True: + try: + pos = next(network_interfaces) + positions.append(max(pos.start() - 1, 0)) + except StopIteration: + break + if positions: + positions.append(len(source_data)) + break + + if not positions: + return available_interfaces + + for l, r in zip(positions, positions[1:]): + chunk = source_data[l:r] + _interface = dict() + for pattern in re_linux + re_openbsd + re_freebsd: + match = re.search(pattern, chunk.replace('\t', '\n')) + if match: + details = match.groupdict() + for k, v in details.items(): + if isinstance(v, str): details[k] = v.strip() + _interface.update(details) + if _interface is not None: + available_interfaces[_interface['name']] = self.update_interface_details(_interface) + + return available_interfaces + + @staticmethod + def update_interface_details(interface): + for attr in IfconfigParser.attributes: + if attr not in interface: + interface[attr] = None + return namedtuple('Interface', interface.keys())(**interface) + + +class InterfaceNotFound(Exception): + """ + """ + pass + + def process(proc_data): """ Final processing to conform to the schema.