###############################################################################
# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems)   #
# and others                                                                  #
#                                                                             #
# All rights reserved. This program and the accompanying materials            #
# are made available under the terms of the Apache License, Version 2.0       #
# which accompanies this distribution, and is available at                    #
# http://www.apache.org/licenses/LICENSE-2.0                                  #
###############################################################################
import re

from discover.fetchers.cli.cli_access import CliAccess
from utils.inventory_mgr import InventoryMgr


class CliFetchHostPnics(CliAccess):
    def __init__(self):
        super().__init__()
        self.inv = InventoryMgr()
        self.ethtool_attr = re.compile('^\s+([^:]+):\s(.*)$')
        self.regexps = [
            {'name': 'mac_address', 're': '^.*\sHWaddr\s(\S+)(\s.*)?$'},
            {'name': 'mac_address', 're': '^.*\sether\s(\S+)(\s.*)?$'},
            {'name': 'IP Address', 're': '^\s*inet addr:?(\S+)\s.*$'},
            {'name': 'IP Address', 're': '^\s*inet ([0-9.]+)\s.*$'},
            {'name': 'IPv6 Address', 're': '^\s*inet6 addr:\s*(\S+)(\s.*)?$'},
            {'name': 'IPv6 Address', 're': '^\s*inet6 \s*(\S+)(\s.*)?$'}
        ]

    def get(self, id):
        host_id = id[:id.rindex("-")]
        cmd = 'ls -l /sys/class/net | grep ^l | grep -v "/virtual/"'
        host = self.inv.get_by_id(self.get_env(), host_id)
        if not host:
            self.log.error("CliFetchHostPnics: host not found: " + host_id)
            return []
        if "host_type" not in host:
            self.log.error("host does not have host_type: " + host_id +
                           ", host: " + str(host))
            return []
        host_types = host["host_type"]
        if "Network" not in host_types and "Compute" not in host_types:
            return []
        interface_lines = self.run_fetch_lines(cmd, host_id)
        interfaces = []
        for line in interface_lines:
            interface_name = line[line.rindex('/')+1:]
            interface_name = interface_name.strip()
            # run ifconfig with specific interface name,
            # since running it with no name yields a list without inactive pNICs
            interface = self.find_interface_details(host_id, interface_name)
            if interface:
                interfaces.append(interface)
        return interfaces

    def find_interface_details(self, host_id, interface_name):
        lines = self.run_fetch_lines("ifconfig " + interface_name, host_id)
        interface = None
        status_up = None
        for line in [l for l in lines if l != '']:
            tokens = None
            if interface is None:
                tokens = line.split()
                name = tokens[0].strip('- :')
                name = name.strip()
                if name == interface_name:
                    line_remainder = line.strip('-')[len(interface_name)+2:]
                    line_remainder = line_remainder.strip(' :')
                    id = interface_name
                    interface = {
                        "host": host_id,
                        "name": id,
                        "local_name": interface_name,
                        "lines": []
                    }
                    self.handle_line(interface, line_remainder)
                    if '<UP,' in line:
                        status_up = True
            if status_up is None:
                if tokens is None:
                    tokens = line.split()
                if 'BROADCAST' in tokens:
                    status_up = 'UP' in tokens
            if interface:
                self.handle_line(interface, line)
        self.set_interface_data(interface)
        interface['state'] = 'UP' if status_up else 'DOWN'
        if 'id' not in interface:
            interface['id'] = interface_name + '-unknown_mac'
        return interface

    def handle_line(self, interface, line):
        self.find_matching_regexps(interface, line, self.regexps)
        if 'mac_address' in interface:
            interface["id"] = interface["name"] + "-" + interface["mac_address"]
        interface["lines"].append(line.strip())

    def set_interface_data(self, interface):
        if not interface:
            return
        interface["data"] = "\n".join(interface["lines"])
        interface.pop("lines", None)
        ethtool_ifname = interface["local_name"]
        if "@" in interface["local_name"]:
            pos = interface["local_name"].index("@")
            ethtool_ifname = ethtool_ifname[pos + 1:]
        cmd = "ethtool " + ethtool_ifname
        lines = self.run_fetch_lines(cmd, interface["host"])
        attr = None
        for line in lines[1:]:
            matches = self.ethtool_attr.match(line)
            if matches:
                # add this attribute to the interface
                attr = matches.group(1)
                value = matches.group(2)
                interface[attr] = value.strip()
            else:
                # add more values to the current attribute as an array
                if isinstance(interface[attr], str):
                    interface[attr] = [interface[attr], line.strip()]
                else:
                    interface[attr].append(line.strip())