#! /usr/bin/python

# Copyright (C) 2019-2020 Cumulus Networks,  Inc. all rights reserved
#
# This software is subject to the Cumulus Networks End User License Agreement available
# at the following locations:
#
# Internet: https://cumulusnetworks.com/downloads/eula/latest/view/
# Cumulus Linux systems: /usr/share/cumulus/EULA.txt

"""
Usage:
  l1-show PORTLIST [--cl-support ROOT] [--json] [--debug MODULES]
  l1-show PORTLIST --pcs-errors [--clear][--json] [--debug MODULES]

  l1-show  -h | --help


Options:
  PORTLIST                  Port to run against e.g swp1,swp2  or all
  -c, --cl-support ROOT     Location of root of uncompressed cl-support file (e.g. ROOT/ has Support/, boot/, etc.)
  -j, --json                Output data to screen in json format
  -v, --debug MODULES       Enable debugging on the list of MODULES. Specify 'main' to debug this module itself, 'all' to debug all
  -p, --pcs-errors          Show low-level Mellanox Phy PCS symbols errors and the FEC correction counters if any.
  --clear                   Clear PCS counters after displaying them
  -h, --help                Show this screen

"""

# For python2 compatibility
from __future__ import print_function

import sys
import logging
import os.path

from docopt import docopt

try:
    from l1_show.l1_show_lib import *
except ImportError:
    from l1_show_lib import *

from module_qualify.parse_port_module_data import parse_bcm_eye
from module_qualify.qualify_utils import swp_sort_pattern

arguments = docopt(__doc__, version='l1-show 0.1')


setup_logging(default_level=logging.ERROR, quiet=True, logformat='%(levelname)s: %(message)s')
this_log = logging.getLogger(__name__)
debug = this_log.debug
info = this_log.info
warn = this_log.warning
error = this_log.error

def critical(mesg):
    this_log.critical(mesg)
    this_log.critical('Exiting abnormally')
    sys.exit(1)

# Convert dict keys to string
# Intended as a replacement for .format with **dict, which can cause errors with `None`
def safe_format(d, s):
    d = {k: str(v) for k, v in d.items()}
    return s.format(**d)


def check_for_override(dut, port, vendor=None, oui=None, pn=None):
    override = None
    if dut.asic_vendor == 'bcm':
        name_pn = str(vendor) + ',' + str(pn)
        oui_pn = str(oui) + ',' + str(pn)
        if name_pn in dut.cable_override:
            override = dut.cable_override[name_pn][0] + '(override)'
        elif oui_pn in dut.cable_override:
            override = dut.cable_override[oui_pn][0] + '(override)'

        if port in dut.port_override:
            override = dut.port_override[port][0] + '(override)'

    return override


def port_info(dut, port):
    return {'port': port}


def module_info(dut, port):
    mod_info_dct = dict(
        vendorName=None,
        vendorPartNumber=None,
        vendorPartRevision=None,
        vendorOUI=None,
        vendorSerialNumber=None,
        identifier=None
    )
    override = None
    eeprom = dut.eeprom_dct.get(port, {})
    debug('EEPROM: ' + pformat(eeprom))
    compliance_codes = get_compliance_codes(eeprom)
    if (
        not compliance_codes
        and dut.eeprom_list and port.replace('swp', '').split('s')[0] not in dut.eeprom_list
    ):
        compliance_codes = ['Fixed']

    debug('Compliance Codes: {}'.format(compliance_codes))
    if port in dut.ethtool_m_dct and dut.ethtool_m_dct[port]:
        mod_info_dct['vendorName'] = dut.ethtool_m_dct[port].get('Vendor name', None)
        mod_info_dct['vendorPartNumber'] = dut.ethtool_m_dct[port].get('Vendor PN', None)
        mod_info_dct['vendorPartRevision'] = dut.ethtool_m_dct[port].get('Vendor rev', None)
        mod_info_dct['vendorOUI'] = dut.ethtool_m_dct[port].get('Vendor OUI', None)
        mod_info_dct['vendorSerialNumber'] = dut.ethtool_m_dct[port].get('Vendor SN', None)
        mod_info_dct['identifier'] = dut.ethtool_m_dct[port].get('Identifier', None)
        override = check_for_override(dut, port,
                                      vendor=mod_info_dct['vendorName'],
                                      oui=mod_info_dct['vendorOUI'],
                                      pn=mod_info_dct['vendorPartNumber'])
    if override:
        debug('Override found: {}'.format(override))
        compliance_codes = [override]
    compliance_codes = convert_compliance_speed(compliance_codes, determine_hw_speed(dut, port))
    mod_info_dct['ethernetComplianceCodes'] = ", ".join(compliance_codes)

    return mod_info_dct


def configured_info(dut, port):
    conf_info_dct = {}
    conf_info_dct['admin'], oper = translate_ip_link_status(dut, port)
    if dut.version[0:1] > '3':
        conf_info_dct['configuredFEC'] = dut.showfec_dct.get(port, {}).get('config_fec', 'Unknown')
    else:
        if dut.asic_vendor == 'mlx':
            conf_info_dct['configuredFEC'] = 'Autodetected'
        elif dut.asic_vendor == 'bcm':
            conf_info_dct['configuredFEC'] = convert_fec_str(dut.showfec_dct.get(port, {}).get('active_fec', 'error'))
        else:
            conf_info_dct['configuredFEC'] = 'Unknown'

    conf_info_dct['kernelSpeed'] = convert_kern_speed(dut.ethtool_dct.get(port, {}).get('speed', 'N/A'))
    conf_info_dct['mtu'] = dut.ip_link_dct.get(port, {}).get('mtu', None)
    conf_info_dct['configuredAutoneg'] = dut.ethtool_dct.get(port, {}).get('autoneg', 'N/A').capitalize()

    return conf_info_dct


def operational_info(dut, port):
    op_info_dct = {}
    if dut.asic_vendor == 'mlx':
        if port not in dut.mlxlink_dct:
            warn("Error: Port {} data not retrieved".format(port))
            op_info_dct['operationalFEC'] = 'error'
            op_info_dct['operationalAutoneg'] = 'error'
            op_info_dct['hardwareLinkstate'] = 'error'
            op_info_dct['hardwareSpeed'] = 'error'

        if not dut.mlxlink_dct[port]:
            debug("Port {} data not retrieved".format(port))
            op_info_dct['operationalFEC'] = 'not_avail'
            op_info_dct['operationalAutoneg'] = 'N/A'
            op_info_dct['hardwareLinkstate'] = 'N/A'
            op_info_dct['hardwareSpeed'] = 'N/A'

        else:
            op_info_dct['operationalFEC'] = convert_fec_str(dut.mlxlink_dct[port].get('Operational Info',
                                                                                      {'FEC': 'not_avail'}).get('FEC', None))
            autoneg, autodetect = convert_mlx_autoneg(dut.mlxlink_dct[port].get('Operational Info',
                                                                                {'Auto Negotiation': 'N/A'}).get('Auto Negotiation', None))
            op_info_dct['operationalAutoneg'] = autoneg
            op_info_dct['hardwareLinkstate'] = 'Up' if dut.mlxlink_dct[port].get('Operational Info', {}).get('State', None) == 'Active' else 'Down'
            op_info_dct['hardwareSpeed'] = determine_hw_speed(dut, port)

    elif dut.asic_vendor == 'bcm':
        bcm_port, unit = dut.porttab_dct[port]
        if dut.ethtool_dct.get(port, {}).get('linkstate', None) != 'yes':
            op_info_dct['operationalFEC'] = 'None (down)'
        else:
            op_info_dct['operationalFEC'] = convert_fec_str(dut.showfec_dct.get(port, {}).get('active_fec', 'error'))
        bcm_autoneg = dut.bcm_portstat_dct.get(unit, {}).get(bcm_port, {}).get('autoneg', None)
        if bcm_autoneg == 'No':
            op_info_dct['operationalAutoneg'] = 'Off'
            if dut.ethtool_dct.get(port, {}).get('autoneg', None) == 'on':
                op_info_dct['operationalAutoneg'] += ' (Autodetect enabld)'
        elif bcm_autoneg == 'Yes':
            op_info_dct['operationalAutoneg'] = 'On'
        else:
            op_info_dct['operationalAutoneg'] = bcm_autoneg

        op_info_dct['hardwareLinkstate'] = 'Up' if dut.bcm_portstat_dct.get(unit, {}).get(bcm_port, {}).get('linkstate', None) == 'up' else 'Down'
        op_info_dct['hardwareSpeed'] = determine_hw_speed(dut, port)

    else:
        error('Asic not determined')
        return 'Error'

    eeprom = dut.eeprom_dct.get(port, {})
    op_info_dct['domTX'] = None
    op_info_dct['domRX'] = None
    dom_dct = get_dom(eeprom)
    if dom_dct:
        debug('dom_dct: {}'.format(dom_dct))
        dom_tx_list = dom_dct.get('TX', ())
        op_info_dct['domTX'] = {i + 1: dom_tx_list[i] for i in range(0, len(dom_tx_list))}
        dom_rx_list = dom_dct.get('RX', ())
        op_info_dct['domRX'] = {i + 1: dom_rx_list[i] for i in range(0, len(dom_rx_list))}

    topo_neighbor_dut, topo_neighbor_port = None, None
    if dut.ptmctl_dct and port in dut.ptmctl_dct and dut.ptmctl_dct[port] and ':' in dut.ptmctl_dct[port]:
        topo_neighbor_dut = str(dut.ptmctl_dct.get(port, ':')).split(':')[0]
        topo_neighbor_port = str(dut.ptmctl_dct.get(port, ':')).split(':')[1]

    op_info_dct['topologyFileNeighbor'] = str(topo_neighbor_dut) + ', ' + str(topo_neighbor_port)

    neighbor_dut, neighbor_port = lldp_remote_port_lookup(dut, port, force_refresh=False)
    op_info_dct['lldpNeighbor'] = str(neighbor_dut) + ', ' + str(neighbor_port)

    op_info_dct['kernelSpeed'] = convert_kern_speed(dut.ethtool_dct.get(port, {}).get('speed', 'N/A'))
    op_info_dct['kernelLinkstate'] = 'Up' if dut.ethtool_dct.get(port, {}).get('linkstate', None) == 'yes' else 'Down'

    return op_info_dct


def bcm_hardware_info(dut, port):
    hw_bcm_dct = {}
    if port in dut.ethmode_dct:
        hw_bcm_dct['ethtoolMode'] = dut.reverse_ethmode_map.get(dut.ethmode_dct[port], 'Unknown')
    else:
        hw_bcm_dct['ethtoolMode'] = 'Not Found'
    bcm_port, unit = dut.porttab_dct[port]
    hw_bcm_dct['localAdvertisedProperties'] = dut.bcm_port_all_dct.get(unit, {}).get(bcm_port, {}).get('Local', None)
    parser, command, _, meta = parse_bcm_eye()
    debug('Getting eye info for bcm_port: {}'.format(bcm_port))
    cmd = command.format(sdk=bcm_port, unit=unit[-1:])
    parsed_dsc = dut.bcmcmd_phy(bcm_port, unit, cmd)
    eye_dct = parser(parsed_dsc)
    debug('eye_dct: {}'.format(eye_dct))
    hw_bcm_dct['bcmEyeSize'] = eye_dct.get('eye', {})
    hw_bcm_dct['bcmHardwareSpeed'] = dut.bcm_portstat_dct.get(unit, {}).get(bcm_port, {}).get('speed', None)
    hw_bcm_dct['carrierDetect'] = 'yes' if dut.bcm_port_all_dct.get(unit, {}).get(bcm_port, {}).get('Up', None) == '*' else 'no'
    hw_bcm_dct['RXfault'] = dut.bcm_port_all_dct.get(unit, {}).get(bcm_port, {}).get('Fault', None)
    hw_bcm_dct['signalDetect'] = (''.join([convert_signal_detect(eye_dct.get('signal_detect', {}).get('signal', {}).get(i, {}).get('detect', ''))
                                   for i in eye_dct.get('signal_detect', {}).get('signal', {})]) or 'N/A')
    hw_bcm_dct['rx_lock'] = (''.join([convert_signal_detect(eye_dct.get('signal_detect', {}).get('signal', {}).get(i, {}).get('lock', ''))
                                     for i in eye_dct.get('signal_detect', {}).get('signal', {})]) or 'N/A')
    hw_bcm_dct['bcmInterfaceMode'] = dut.bcm_portstat_dct.get(unit, {}).get(bcm_port, {}).get('cable', None)
    hw_bcm_dct['hw_active_fec'] = convert_hw_fec_str(dut.showfec_dct.get(port, {}).get('hw_active_fec', 'error'))
    if dut.bcm_portstat_dct.get(unit, {}).get(bcm_port, {}).get('autoneg', None) == 'Yes':
        hw_bcm_dct['bcmHardwareAutoneg'] = 'On'
    else:
        hw_bcm_dct['bcmHardwareAutoneg'] = 'Off'

    hw_bcm_dct['mdix'] = dut.bcm_port_all_dct.get(unit, {}).get(bcm_port, {}).get('MDIX', None)
    hw_bcm_dct['remoteAdvertisedProperties'] = dut.bcm_port_all_dct.get(unit, {}).get(bcm_port, {}).get('Remote', None)

    return hw_bcm_dct


def mlx_hardware_info(dut, port):
    hw_mlx_dct = {}
    if port not in dut.mlxlink_dct:
        return "Error: Port {} data not retrieved".format(port)

    if not dut.mlxlink_dct[port]:
        warn("mlxlinkPort {} data not available".format(port))

    debug('mlxlink_dct[port]: {}'.format(dut.mlxlink_dct[port]))
    # hw_speed_raw = dut.mlxlink_dct[port]['Operational Info'].get('Speed', 'None').replace('bE', '')
    # hw_mlx_dct['mlxHardwareSpeed'] = '1G' if hw_speed_raw == 'CX' else hw_speed_raw
    hw_mlx_dct['mlxHardwareSpeed'] = determine_hw_speed(dut, port)
    hw_mlx_dct['mlxComplianceCode'] = dut.mlxlink_dct[port].get('Module Info', {}).get('Compliance', None)
    hw_mlx_dct['mlxCableType'] = dut.mlxlink_dct[port].get('Module Info', {}).get('Cable Type', None)
    autoneg, autodetect = convert_mlx_autoneg(dut.mlxlink_dct[port].get('Operational Info',
                                                                        {'Auto Negotiation': 'N/A'}).get('Auto Negotiation', None))
    hw_mlx_dct['mlxHardwareAutodetect'] = autodetect
    eyes = dut.mlxlink_dct[port].get('EYE Opening Info', {}).get('Height Eye Opening [mV]', 'N/A')
    hw_mlx_dct['mlxEyeHeight'] = ', '.join([eye.strip() for eye in eyes.split(',')])
    grades = dut.mlxlink_dct[port].get('EYE Opening Info', {}).get('Physical Grade', 'N/A')
    hw_mlx_dct['mlxEyeGrade'] = ', '.join([grade.strip() for grade in grades.split(',')])
    hw_mlx_dct['troubleshootingInfo'] = dut.mlxlink_dct[port].get('Troubleshooting Info', {}).get('Recommendation', None)

    return hw_mlx_dct


def format_port_info(port_dct):
    return safe_format(port_dct, """Port:  {port}""")


def format_module_info(mod_info_dct):
    return safe_format(mod_info_dct, """  Module Info
      Vendor Name: {vendorName:22s} PN: {vendorPartNumber}
      Identifier: {identifier:23s} Type: {ethernetComplianceCodes}""")


def format_configured_info(conf_dct):
    return safe_format(conf_dct, """  Configured State
      Admin: {admin:12s} Speed: {kernelSpeed:8s} MTU: {mtu}
      Autoneg: {configuredAutoneg:26s} FEC: {configuredFEC}""")


def format_operational_info(op_info_dct):
    if op_info_dct['domTX']:
        op_info_dct['domTX'] = str([op_info_dct['domTX'][i] for i in range(1, len(op_info_dct['domTX']) + 1)])
    if op_info_dct['domRX']:
        op_info_dct['domRX'] = str([op_info_dct['domRX'][i] for i in range(1, len(op_info_dct['domRX']) + 1)])
    return safe_format(op_info_dct, """  Operational State
      Link Status: Kernel: {kernelLinkstate:14s} Hardware: {hardwareLinkstate}
      Speed: Kernel: {kernelSpeed:20s} Hardware: {hardwareSpeed}
      Autoneg: {operationalAutoneg:26s} FEC: {operationalFEC}
      TX Power (mW): {domTX}
      RX Power (mW): {domRX}
      Topo File Neighbor: {topologyFileNeighbor}
      LLDP Neighbor:      {lldpNeighbor}""")


def format_bcm_hardware_info(hw_bcm_dct):
    if len(str(hw_bcm_dct['localAdvertisedProperties'])) > 20:
        hw_bcm_dct['localAdvertisedProperties'] = ' ' + str(hw_bcm_dct['localAdvertisedProperties']) + '\n     '

    eye_out = []
    eye_out_formatted = ''
    if hw_bcm_dct['bcmEyeSize']:
        for lane in sorted(hw_bcm_dct['bcmEyeSize']):
            lane_dct = hw_bcm_dct['bcmEyeSize'][lane]
            eye_out.append('L: {left}, R: {right}, U: {up}, D: {down}'.format(
                left=lane_dct['L'], right=lane_dct['R'], up=lane_dct['U'], down=lane_dct['D']))
        for i, eye in enumerate(eye_out):
            if i % 2 == 1:
                eye_out_formatted += '{},\n            '.format(eye)
            else:
                eye_out_formatted += '{}, '.format(eye)
        hw_bcm_dct['bcmEyeSize'] = eye_out_formatted.strip()[:-1]
    else:
        hw_bcm_dct['bcmEyeSize'] = 'N/A'

    if '1G' in str(hw_bcm_dct['bcmHardwareSpeed']):
        hw_bcm_dct['RXfault'] = str(hw_bcm_dct['RXfault']) + ' (ignore on 1G)'

    return safe_format(hw_bcm_dct, """  Port Hardware State:
      Rx Fault: {RXfault:25s} Carrier Detect: {carrierDetect}
      Rx Signal: Detect: {signalDetect:16s} Signal Lock: {rx_lock}
      Ethmode Type: {ethtoolMode:21s} Interface Type: {bcmInterfaceMode}
      Speed: {bcmHardwareSpeed:28s} Autoneg: {bcmHardwareAutoneg}
      MDIX: {mdix:29s} FEC: {hw_active_fec}
      Local Advrtsd: {localAdvertisedProperties:20s} Remote Advrtsd: {remoteAdvertisedProperties}
      Eyes: {bcmEyeSize}""")


def format_mlx_hardware_info(hw_mlx_dct):
    return safe_format(hw_mlx_dct, """  Port Hardware State:
      Compliance Code: {mlxComplianceCode}
      Cable Type: {mlxCableType}
      Speed: {mlxHardwareSpeed:28s} Autodetect: {mlxHardwareAutodetect}
      Eyes: {mlxEyeHeight:29s} Grade: {mlxEyeGrade}
      Troubleshooting Info: {troubleshootingInfo}""")


def format_pcs_info(pcs_counters_port_dct):
    return safe_format(pcs_counters_port_dct, """  PCS error counters:
      time_since_last_clear : {time_since_last_clear}
      phy_received_bits       : {phy_received_bits}
      phy_symbol_errors       : {phy_symbol_errors}
      phy_corrected_bits      : {phy_corrected_bits}
      phy_raw_errors_lane0    : {phy_raw_errors_lane0}
      phy_raw_errors_lane1    : {phy_raw_errors_lane1}
      phy_raw_errors_lane2    : {phy_raw_errors_lane2}
      phy_raw_errors_lane3    : {phy_raw_errors_lane3}
      phy_raw_errors_lane4    : {phy_raw_errors_lane4}
      phy_raw_errors_lane5    : {phy_raw_errors_lane5}
      phy_raw_errors_lane6    : {phy_raw_errors_lane6}
      phy_raw_errors_lane7    : {phy_raw_errors_lane7}
      raw_ber_magnitude       : {raw_ber_magnitude}
      raw_ber_coef            : -{raw_ber_coef}
      effective_ber_magnitude : {effective_ber_magnitude}
      effective_ber_coef      : -{effective_ber_coef}""")


def create_all_ports_list(dut, cl_support):
    prefix = cl_support if cl_support is not None else '/'
    # Match groups are used for natural sort - 1st group is whole match
    path = os.path.join(prefix, 'sys/class/net/')
    if os.path.exists(path):
        p = re.compile(r'sw[0-9]*p[0-9]+(s[0-3])?$')
        matches = [f for f in os.listdir(path) if p.match(f)]
    elif cl_support:
        # Try to get from ip.link, /sys/class/net not available in older cl_supports
        matches = dut.ip_link_show()
    else:
        critical('Node /sys/class/net and /Support/ip.addr does not exist')

    allportslist = sorted(matches, key=lambda l: swp_sort_pattern(l))

    return allportslist


def filter_port_list(allportslist, port_filter):
    if port_filter == 'all':
        debug('Portlist is {}'.format(allportslist))
        return allportslist

    port_filter = expand_port_filter(port_filter)

    sanitized_portlist = []
    for port in port_filter:
        if re.match('^[0-9]+(s[0123])?$', port):
            port = 'swp' + port
        if re.match('^[0-9]+p[0-9]+(s[0-3])?$', port):
            port = 'sw' + port
        if not re.match('^sw[0-9]*p[0-9]+(s[0-3])?$', port):
            error('Port: \'{}\' not formatted correctly. Skipping.'.format(port))
            continue
        if port not in allportslist:
            error('Port: \'{}\' does not exist on this switch. Skipping.'.format(port))
            continue
        if (os.path.isdir("/sys/class/net/{}/bridge".format(port))
                or os.path.isdir("/sys/class/net/{}/bonding".format(port))):
            warn('Port: \'{}\' is not a physical interface. Skipping.'.format(port))
            continue

        sanitized_portlist.append(port)

    debug('Portlist is {}'.format(sanitized_portlist))
    return sanitized_portlist


def expand_port_filter(port_filter):
    port_filter = port_filter.split(',')
    port_list = []
    for port_group in port_filter:
        if '-' in port_group:
            rangelist = port_group.split('-')
            for i in range(int(rangelist[0]), int(rangelist[1]) + 1):
                port_list.append(str(i))
        else:
            port_list.append(port_group)
    return port_list


def collect_all_l1_data(dut, portlist):
    dut_install_version(dut)
    asic_vendor = dut.asic_vendor
    if asic_vendor == 'bcm':
        debug('Starting porttab collection')
        dut_install_porttab(dut)
        debug('Starting portstat collection')
        dut_install_bcm_dct(dut, 'portstat')
        debug('Starting port all collection')
        dut_install_bcm_dct(dut, 'port all')
        debug('Setting up portwd overrides')
        setup_overrides(dut)
        debug('Starting ethmodes collection')
        dut_install_ethmodes(dut, portlist)

    elif asic_vendor == 'mlx':
        debug('Starting mlxlink collection')
        dut.mlxlink(portlist)

    elif asic_vendor == 'vx':
        error('L1-show is not a valid command for Vx switches.')
        sys.exit(1)

    else:
        error('Unrecognized asic vendor \'{}\' - not supported.'.format(asic_vendor))
        sys.exit(1)
    debug('Starting ethtool collection')
    dut_install_ethtool(dut, portlist)
    debug('Starting ethtool -m collection')
    dut_install_ethtool_m(dut, portlist)
    debug(dut.ethtool_m_dct)
    debug('Starting eeprom collection')
    dut_install_eeprom(dut, portlist)
    debug('Starting ptmctl collection')
    dut_install_ptmctl(dut)
    debug('Starting lldp collection')
    dut_install_lldpctl(dut)
    debug('Staring ethtool --show-fec collection')
    dut_install_ethtool_showfec(dut, portlist)
    dut_install_ip_link(dut)

    return asic_vendor


def l1_show_output_to_screen(asic_vendor, dut, portlist, pcs_data=False):
    for port in portlist:
        print(format_port_info(port_info(dut, port)))
        if pcs_data:
            print(format_pcs_info(dut.pcs_counters_dct.get(port, {})))
        else:
            print(format_module_info(module_info(dut, port)))
            print(format_configured_info(configured_info(dut, port)))
            print(format_operational_info(operational_info(dut, port)))

            if asic_vendor == 'bcm':
                print(format_bcm_hardware_info(bcm_hardware_info(dut, port)))

            elif asic_vendor == 'mlx':
                print(format_mlx_hardware_info(mlx_hardware_info(dut, port)))


def l1_show_output_to_json(asic_vendor, dut, portlist, pcs_data=False):
    l1_show_dct = {}
    for port in portlist:
        l1_show_dct[port] = {}
        l1_show_dct[port]['portInfo'] = port_info(dut, port)
        if pcs_data:
            l1_show_dct[port]['pcsErrors'] = dut.pcs_counters_dct.get(port, {})
        else:
            l1_show_dct[port]['moduleInfo'] = module_info(dut, port)
            l1_show_dct[port]['configuredInfo'] = configured_info(dut, port)
            l1_show_dct[port]['operationalInfo'] = operational_info(dut, port)
            if asic_vendor == 'bcm':
                l1_show_dct[port]['hardwareInfoBroadcom'] = bcm_hardware_info(dut, port)
            elif asic_vendor == 'mlx':
                l1_show_dct[port]['hardwareInfoMellanox'] = mlx_hardware_info(dut, port)

    return json.dumps(l1_show_dct, indent=4)


def main():

    cl_support = arguments['--cl-support']

    if (not os.geteuid() == 0) and (cl_support is None):
        print('Error: If running on a switch, command must be run as root')
        print('Error: If running on a cl-support file, must supply a directory')
        print('Error: See l1-show -h for more')
        sys.exit(1)

    if arguments['--debug']:
        debug_logging(arguments['--debug'].split(','), this_log)
        list_logger_levels()

    debug('Argument: {}'.format(arguments))

    if cl_support is not None:
        # Checks that cl-support exists and that we can read it
        if not os.path.isdir(cl_support):
            print('Error: "{cl_support}" is not a directory'.format(cl_support=cl_support))
            sys.exit(1)
        if not os.access(cl_support, os.R_OK):
            print('Error: "{cl_support}" is not readable'.format(cl_support=cl_support))
            sys.exit(1)

    dut = Dut(cl_support)

    if arguments['PORTLIST']:
        port_filter = arguments['PORTLIST']
        allportslist = create_all_ports_list(dut, cl_support)
        # dut.create_port_list(arguments['PORTLIST'].split(','))

        if not allportslist:
            error('No valid ports')
            sys.exit(1)
    else:
        error('Must specify a port list or "all"')
        sys.exit(1)

    portlist = filter_port_list(allportslist, port_filter)
    debug('[outer routine] portlist is {}'.format(portlist))

    pcs_data = arguments['--pcs-errors']
    clear = arguments['--clear']
    if portlist:
        if pcs_data:
            if dut.asic_vendor != 'mlx':
                error('pcs-error flag only supported on Spectrum ASICs')
                sys.exit(1)
            else:
                debug('Starting linux to mlx mapping collection')
                dut.install_linux_to_mlx()
                debug('Starting pcs counter collection')
                dut.install_pcs_data(portlist, arguments['--clear'])
        else:
            collect_all_l1_data(dut, portlist)

        if arguments['--json']:
            print(l1_show_output_to_json(dut.asic_vendor, dut, portlist, pcs_data=pcs_data))
            if clear:
                print('PCS error counters cleared')
        else:
            l1_show_output_to_screen(dut.asic_vendor, dut, portlist, pcs_data=pcs_data)


if __name__ == '__main__':
    sys.exit(main())
