From cbd956d9454183cd7bbd88a9a1da91195b4e2152 Mon Sep 17 00:00:00 2001 From: nikos-li <31227248+nikos-li@users.noreply.github.com> Date: Wed, 22 Nov 2017 16:55:52 -0800 Subject: [PATCH 01/32] FRR: fix show bgp and clear bgp to provide the full set of cli options available (#149) --- clear/bgp_frr_v4.py | 226 -------------------------------------------- clear/bgp_frr_v6.py | 114 ---------------------- clear/main.py | 23 +++-- show/bgp_frr_v4.py | 127 ------------------------- show/bgp_frr_v6.py | 65 ------------- show/main.py | 18 ++-- 6 files changed, 27 insertions(+), 546 deletions(-) delete mode 100644 clear/bgp_frr_v4.py delete mode 100644 clear/bgp_frr_v6.py delete mode 100644 show/bgp_frr_v4.py delete mode 100644 show/bgp_frr_v6.py diff --git a/clear/bgp_frr_v4.py b/clear/bgp_frr_v4.py deleted file mode 100644 index 2446792d21..0000000000 --- a/clear/bgp_frr_v4.py +++ /dev/null @@ -1,226 +0,0 @@ -import click -from clear.main import * - - -############################################################################### -# -# 'clear bgp' cli stanza -# -############################################################################### - - -@cli.group(cls=AliasedGroup, default_if_no_args=True) -def bgp(): - """Clear BGP peers / state""" - pass - - -# Default 'bgp' command (called if no subcommands or their aliases were passed) -@bgp.command(default=True) -def default(): - """Clear all BGP peers""" - command = 'sudo vtysh -c "clear bgp *"' - run_command(command) - - -@bgp.group(cls=AliasedGroup, default_if_no_args=True, - context_settings=CONTEXT_SETTINGS) -def neighbor(): - """Clear specific BGP peers""" - pass - - -@neighbor.command(default=True) -@click.argument('ipaddress', required=False) -def default(ipaddress): - """Clear all BGP peers""" - - if ipaddress is not None: - command = 'sudo vtysh -c "clear bgp {} "'.format(ipaddress) - else: - command = 'sudo vtysh -c "clear bgp *"' - run_command(command) - - -# 'in' subcommand -@neighbor.command('in') -@click.argument('ipaddress', required=False) -def neigh_in(ipaddress): - """Send route-refresh""" - - if ipaddress is not None: - command = 'sudo vtysh -c "clear bgp {} in"'.format(ipaddress) - else: - command = 'sudo vtysh -c "clear bgp * in"' - run_command(command) - - -# 'out' subcommand -@neighbor.command('out') -@click.argument('ipaddress', required=False) -def neigh_out(ipaddress): - """Resend all outbound updates""" - - if ipaddress is not None: - command = 'sudo vtysh -c "clear bgp {} out"'.format(ipaddress) - else: - command = 'sudo vtysh -c "clear bgp * out"' - run_command(command) - - -@neighbor.group(cls=AliasedGroup, default_if_no_args=True, - context_settings=CONTEXT_SETTINGS) -def soft(): - """Soft reconfig BGP's inbound/outbound updates""" - pass - - -@soft.command(default=True) -@click.argument('ipaddress', required=False) -def default(ipaddress): - """Clear BGP peers soft configuration""" - - if ipaddress is not None: - command = 'sudo vtysh -c "clear bgp {} soft "'.format(ipaddress) - else: - command = 'sudo vtysh -c "clear bgp * soft"' - run_command(command) - - -# 'soft in' subcommand -@soft.command('in') -@click.argument('ipaddress', required=False) -def soft_in(ipaddress): - """Send route-refresh""" - - if ipaddress is not None: - command = 'sudo vtysh -c "clear bgp {} soft in"'.format(ipaddress) - else: - command = 'sudo vtysh -c "clear bgp * soft in"' - run_command(command) - - -# 'soft out' subcommand -@soft.command('out') -@click.argument('ipaddress', required=False) -def soft_out(ipaddress): - """Resend all outbound updates""" - - if ipaddress is not None: - command = 'sudo vtysh -c "clear bgp {} soft out"'.format(ipaddress) - else: - command = 'sudo vtysh -c "clear bgp * soft out"' - run_command(command) - - -############################################################################### -# -# 'clear bgp ipv4' cli stanza -# -############################################################################### - - -@bgp.group(cls=AliasedGroup, default_if_no_args=True, - context_settings=CONTEXT_SETTINGS) -def ipv4(): - """Clear BGP IPv4 peers / state""" - pass - - -# Default 'bgp' command (called if no subcommands or their aliases were passed) -@ipv4.command(default=True) -def default(): - """Clear all IPv4 BGP peers""" - command = 'sudo vtysh -c "clear bgp ipv4 *"' - run_command(command) - - -@ipv4.group(cls=AliasedGroup, default_if_no_args=True, - context_settings=CONTEXT_SETTINGS) -def neighbor(): - """Clear specific IPv4 BGP peers""" - pass - - -@neighbor.command(default=True) -@click.argument('ipaddress', required=False) -def default(ipaddress): - """Clear all IPv4 BGP peers""" - - if ipaddress is not None: - command = 'sudo vtysh -c "clear bgp ipv4 {} "'.format(ipaddress) - else: - command = 'sudo vtysh -c "clear bgp ipv4 *"' - run_command(command) - - -# 'in' subcommand -@neighbor.command('in') -@click.argument('ipaddress', required=False) -def neigh_in(ipaddress): - """Send route-refresh""" - - if ipaddress is not None: - command = 'sudo vtysh -c "clear bgp ipv4 {} in"'.format(ipaddress) - else: - command = 'sudo vtysh -c "clear bgp ipv4 * in"' - run_command(command) - - -# 'out' subcommand -@neighbor.command('out') -@click.argument('ipaddress', required=False) -def neigh_out(ipaddress): - """Resend all outbound updates""" - - if ipaddress is not None: - command = 'sudo vtysh -c "clear bgp ipv4 {} out"'.format(ipaddress) - else: - command = 'sudo vtysh -c "clear bgp ipv4 * out"' - run_command(command) - - -@neighbor.group(cls=AliasedGroup, default_if_no_args=True, - context_settings=CONTEXT_SETTINGS) -def soft(): - """Soft reconfig BGP's inbound/outbound updates""" - pass - - -@soft.command(default=True) -@click.argument('ipaddress', required=False) -def default(ipaddress): - """Clear BGP neighbors soft configuration""" - - if ipaddress is not None: - command = 'sudo vtysh -c "clear bgp ipv4 {} soft "'.format(ipaddress) - else: - command = 'sudo vtysh -c "clear bgp ipv4 * soft"' - run_command(command) - - -# 'soft in' subcommand -@soft.command('in') -@click.argument('ipaddress', required=False) -def soft_in(ipaddress): - """Send route-refresh""" - - if ipaddress is not None: - command = 'sudo vtysh -c "clear bgp ipv4 {} soft in"'.format(ipaddress) - else: - command = 'sudo vtysh -c "clear bgp ipv4 * soft in"' - run_command(command) - - -# 'soft out' subcommand -@soft.command('out') -@click.argument('ipaddress', required=False) -def soft_out(ipaddress): - """Resend all outbound updates""" - - if ipaddress is not None: - command = 'sudo vtysh -c "clear bgp ipv4 {} soft out"'.\ - format(ipaddress) - else: - command = 'sudo vtysh -c "clear bgp ipv4 * soft out"' - run_command(command) diff --git a/clear/bgp_frr_v6.py b/clear/bgp_frr_v6.py deleted file mode 100644 index a08453b043..0000000000 --- a/clear/bgp_frr_v6.py +++ /dev/null @@ -1,114 +0,0 @@ -import click -from clear.main import * - - -############################################################################### -# -# 'clear bgp ipv6' cli stanza -# -############################################################################### - - -@bgp.group(cls=AliasedGroup, default_if_no_args=True) -def ipv6(): - """Clear BGP IPv6 peers / state""" - pass - - -# Default 'bgp' command (called if no subcommands or their aliases were passed) -@ipv6.command(default=True) -def default(): - """Clear all IPv6 BGP peers""" - command = 'sudo vtysh -c "clear bgp ipv6 *"' - run_command(command) - - -@ipv6.group(cls=AliasedGroup, default_if_no_args=True, - context_settings=CONTEXT_SETTINGS) -def neighbor(): - """Clear specific IPv6 BGP peers""" - pass - - -@neighbor.command(default=True) -@click.argument('ipaddress', required=False) -def default(ipaddress): - """Clear all IPv6 BGP peers""" - - if ipaddress is not None: - command = 'sudo vtysh -c "clear bgp ipv6 {} "'.format(ipaddress) - else: - command = 'sudo vtysh -c "clear bgp ipv6 *"' - run_command(command) - - -# 'in' subcommand -@neighbor.command('in') -@click.argument('ipaddress', required=False) -def neigh_in(ipaddress): - """Send route-refresh""" - - if ipaddress is not None: - command = 'sudo vtysh -c "clear bgp ipv6 {} in"'.format(ipaddress) - else: - command = 'sudo vtysh -c "clear bgp ipv6 * in"' - run_command(command) - - -# 'out' subcommand -@neighbor.command('out') -@click.argument('ipaddress', required=False) -def neigh_out(ipaddress): - """Resend all outbound updates""" - - if ipaddress is not None: - command = 'sudo vtysh -c "clear bgp ipv6 {} out"'.format(ipaddress) - else: - command = 'sudo vtysh -c "clear bgp ipv6 * out"' - run_command(command) - - -@neighbor.group(cls=AliasedGroup, default_if_no_args=True, - context_settings=CONTEXT_SETTINGS) -def soft(): - """Soft reconfig BGP's inbound/outbound updates""" - pass - - -@soft.command(default=True) -@click.argument('ipaddress', required=False) -def default(ipaddress): - """Clear BGP neighbors soft configuration""" - - if ipaddress is not None: - command = 'sudo vtysh -c "clear bgp ipv6 {} soft "'.format(ipaddress) - else: - command = 'sudo vtysh -c "clear bgp ipv6 * soft"' - run_command(command) - - -# 'soft in' subcommand -@soft.command('in') -@click.argument('ipaddress', required=False) -def soft_in(ipaddress): - """Send route-refresh""" - - if ipaddress is not None: - command = 'sudo vtysh -c "clear bgp ipv6 {} soft in"'.format(ipaddress) - else: - command = 'sudo vtysh -c "clear bgp ipv6 * soft in"' - run_command(command) - - -# 'soft out' subcommand -@soft.command('out') -@click.argument('ipaddress', required=False) -def soft_out(ipaddress): - """Resend all outbound updates""" - - if ipaddress is not None: - command = 'sudo vtysh -c "clear bgp ipv6 {} soft out"' \ - .format(ipaddress) - else: - command = 'sudo vtysh -c "clear bgp ipv6 * soft out"' - run_command(command) diff --git a/clear/main.py b/clear/main.py index 32ced2177e..d74947967d 100755 --- a/clear/main.py +++ b/clear/main.py @@ -151,9 +151,8 @@ def ipv6(): # -# Inserting BGP functionality into cli's clear parse-chain. The insertion point -# and the specific BGP commands to import, will be determined by the routing-stack -# being elected. +# Inserting BGP functionality into cli's clear parse-chain. +# BGP commands are determined by the routing-stack being elected. # if routing_stack == "quagga": from .bgp_quagga_v4 import bgp @@ -161,10 +160,20 @@ def ipv6(): from .bgp_quagga_v6 import bgp ipv6.add_command(bgp) elif routing_stack == "frr": - from .bgp_frr_v4 import bgp - cli.add_command(bgp) - from .bgp_frr_v6 import bgp - cli.add_command(bgp) + @cli.command() + @click.argument('bgp_args', nargs = -1, required = False) + def bgp(bgp_args): + """BGP information""" + bgp_cmd = "clear bgp" + options = False + for arg in bgp_args: + bgp_cmd += " " + str(arg) + options = True + if options is True: + command = 'sudo vtysh -c "{}"'.format(bgp_cmd) + else: + command = 'sudo vtysh -c "clear bgp *"' + run_command(command) @cli.command() diff --git a/show/bgp_frr_v4.py b/show/bgp_frr_v4.py deleted file mode 100644 index ba76b17f03..0000000000 --- a/show/bgp_frr_v4.py +++ /dev/null @@ -1,127 +0,0 @@ -import click -from show.main import * - - -############################################################################### -# -# 'show bgp' cli stanza -# -############################################################################### - - -@cli.group(cls=AliasedGroup, default_if_no_args=False) -def bgp(): - """Show IPv4 BGP (Border Gateway Protocol) information""" - pass - - -# 'summary' subcommand ("show bgp summary") -@bgp.command() -def summary(): - """Show summarized information of IPv4 BGP state""" - run_command('sudo vtysh -c "show bgp summary"') - - -# 'neighbors' subgroup ("show bgp neighbors ...") -@bgp.group(cls=AliasedGroup, default_if_no_args=True) -def neighbors(): - """Show BGP neighbors""" - pass - - -# 'neighbors' subcommand ("show bgp neighbors") -@neighbors.command(default=True) -@click.argument('ipaddress', required=False) -def default(ipaddress): - """Show BGP neighbors""" - - if ipaddress is not None: - command = 'sudo vtysh -c "show bgp neighbor {} "'.format(ipaddress) - run_command(command) - else: - run_command('sudo vtysh -c "show bgp neighbor"') - - -# 'advertised-routes' subcommand ("show bgp neighbors advertised-routes ") -@neighbors.command('advertised-routes') -@click.argument('ipaddress') -def advertised_routes(ipaddress): - """Display routes advertised to a BGP peer""" - - command = 'sudo vtysh -c "show bgp ipv4 neighbor {} advertised-routes"'. \ - format(ipaddress) - run_command(command) - - -# 'routes' subcommand ("show bgp neighbors routes ") -@neighbors.command('routes') -@click.argument('ipaddress') -def routes(ipaddress): - """Display routes learned from neighbor""" - - command = 'sudo vtysh -c "show bgp ipv4 neighbor {} routes"'. \ - format(ipaddress) - run_command(command) - - -############################################################################### -# -# 'show bgp ipv4' cli stanza -# -############################################################################### - - -@bgp.group(cls=AliasedGroup, default_if_no_args=False) -def ipv4(): - """Show BGP ipv4 commands""" - pass - - -# 'summary' subcommand ("show bgp ipv4 summary") -@ipv4.command() -def summary(): - """Show summarized information of IPv4 BGP state""" - run_command('sudo vtysh -c "show bgp ipv4 summary"') - - -# 'neighbors' subgroup ("show bgp ipv4 neighbors ...") -@ipv4.group(cls=AliasedGroup, default_if_no_args=True) -def neighbors(): - """Show BGP IPv4 neighbors""" - pass - - -# 'neighbors' subcommand ("show bgp ipv4 neighbors") -@neighbors.command(default=True) -@click.argument('ipaddress', required=False) -def default(ipaddress): - """Show BGP IPv4 neighbors""" - - if ipaddress is not None: - command = 'sudo vtysh -c "show bgp ipv4 neighbor {} "'. \ - format(ipaddress) - run_command(command) - else: - run_command('sudo vtysh -c "show bgp ipv4 neighbor"') - - -# 'advertised-routes' subcommand ("show bgp ipv4 neighbors advertised-routes ") -@neighbors.command('advertised-routes') -@click.argument('ipaddress') -def advertised_routes(ipaddress): - """Display routes advertised to a BGP peer""" - - command = 'sudo vtysh -c "show bgp ipv4 neighbor {} advertised-routes"'. \ - format(ipaddress) - run_command(command) - - -# 'routes' subcommand ("show bgp ipv4 neighbors routes ") -@neighbors.command('routes') -@click.argument('ipaddress') -def routes(ipaddress): - """Display routes learned from neighbor""" - - command = 'sudo vtysh -c "show bgp ipv4 neighbor {} routes"'. \ - format(ipaddress) - run_command(command) diff --git a/show/bgp_frr_v6.py b/show/bgp_frr_v6.py deleted file mode 100644 index 8627461b83..0000000000 --- a/show/bgp_frr_v6.py +++ /dev/null @@ -1,65 +0,0 @@ -import click -from show.main import * - - -############################################################################### -# -# 'show bgp ipv6' cli stanza -# -############################################################################### - - -@bgp.group(cls=AliasedGroup, default_if_no_args=False) -def ipv6(): - """Show BGP ipv6 commands""" - pass - - -# 'summary' subcommand ("show bgp ipv6 summary") -@ipv6.command() -def summary(): - """Show summarized information of IPv6 BGP state""" - run_command('sudo vtysh -c "show bgp ipv6 summary"') - - -# 'neighbors' subgroup ("show bgp ipv6 neighbors ...") -@ipv6.group(cls=AliasedGroup, default_if_no_args=True) -def neighbors(): - """Show BGP IPv6 neighbors""" - pass - - -# 'neighbors' subcommand ("show bgp ipv6 neighbors") -@neighbors.command(default=True) -@click.argument('ipaddress', required=False) -def default(ipaddress): - """Show BGP IPv6 neighbors""" - - if ipaddress is not None: - command = 'sudo vtysh -c "show bgp ipv6 neighbor {} "'. \ - format(ipaddress) - run_command(command) - else: - run_command('sudo vtysh -c "show bgp ipv6 neighbor"') - - -# 'advertised-routes' subcommand ("show bgp ipv6 neighbors advertised-routes ") -@neighbors.command('advertised-routes') -@click.argument('ipaddress') -def advertised_routes(ipaddress): - """Display routes advertised to a BGP peer""" - - command = 'sudo vtysh -c "show bgp ipv6 neighbor {} advertised-routes"'. \ - format(ipaddress) - run_command(command) - - -# 'routes' subcommand ("show bgp ipv6 neighbors routes ") -@neighbors.command('routes') -@click.argument('ipaddress') -def routes(ipaddress): - """Display routes learned from neighbor""" - - command = 'sudo vtysh -c "show bgp ipv6 neighbor {} routes"'. \ - format(ipaddress) - run_command(command) diff --git a/show/main.py b/show/main.py index 38111ab2be..5a2399c833 100755 --- a/show/main.py +++ b/show/main.py @@ -383,9 +383,8 @@ def protocol(): # -# Inserting BGP functionality into cli's show parse-chain. The insertion point -# and the specific BGP commands to import, will be determined by the routing-stack -# being elected. +# Inserting BGP functionality into cli's show parse-chain. +# BGP commands are determined by the routing-stack being elected. # if routing_stack == "quagga": from .bgp_quagga_v4 import bgp @@ -393,10 +392,15 @@ def protocol(): from .bgp_quagga_v6 import bgp ipv6.add_command(bgp) elif routing_stack == "frr": - from .bgp_frr_v4 import bgp - cli.add_command(bgp) - from .bgp_frr_v6 import bgp - cli.add_command(bgp) + @cli.command() + @click.argument('bgp_args', nargs = -1, required = False) + def bgp(bgp_args): + """BGP information""" + bgp_cmd = "show bgp" + for arg in bgp_args: + bgp_cmd += " " + str(arg) + command = 'sudo vtysh -c "{}"'.format(bgp_cmd) + run_command(command) # From 0762204aa3d715b9d3bc4cae3cdfa219f3bcdef5 Mon Sep 17 00:00:00 2001 From: Taoyu Li Date: Wed, 22 Nov 2017 17:41:35 -0800 Subject: [PATCH 02/32] [config] Call hostname service to config hostname (#154) [config] Call hostname service to config hostname instead --- config/main.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/config/main.py b/config/main.py index ace8643b99..1e8fa1f30c 100644 --- a/config/main.py +++ b/config/main.py @@ -94,6 +94,7 @@ def _abort_if_false(ctx, param, value): ctx.abort() def _restart_services(): + run_command("service hostname-config restart", display_cmd=True) run_command("service interfaces-config restart", display_cmd=True) run_command("service ntp-config restart", display_cmd=True) run_command("service rsyslog-config restart", display_cmd=True) @@ -143,11 +144,6 @@ def reload(filename): command = "{} -j {} --write-to-db".format(SONIC_CFGGEN_PATH, filename) run_command(command, display_cmd=True) client.set(config_db.INIT_INDICATOR, True) - command = "{} -j {} -v \"DEVICE_METADATA['localhost']['hostname']\"".format(SONIC_CFGGEN_PATH, filename) - p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) - p.wait() - hostname = p.communicate()[0].strip() - _change_hostname(hostname) _restart_services() @cli.command() @@ -189,14 +185,10 @@ def load_minigraph(): command = "{} -m --write-to-db".format(SONIC_CFGGEN_PATH) run_command(command, display_cmd=True) client.set(config_db.INIT_INDICATOR, True) - command = "{} -m -v \"DEVICE_METADATA['localhost']['hostname']\"".format(SONIC_CFGGEN_PATH) - p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) - p.wait() - hostname = p.communicate()[0].strip() - _change_hostname(hostname) #FIXME: After config DB daemon is implemented, we'll no longer need to restart every service. _restart_services() print "Please note setting loaded from minigraph will be lost after system reboot. To preserve setting, run `config save`." + # # 'bgp' group # From fb48cdf0e26219de1f9b958db49c8afd02ca12da Mon Sep 17 00:00:00 2001 From: AndriiS Date: Tue, 28 Nov 2017 22:00:53 +0200 Subject: [PATCH 03/32] Added PSU CLI (#152) --- data/etc/bash_completion.d/psuutil | 8 ++ psuutil/__init__.py | 0 psuutil/main.py | 192 +++++++++++++++++++++++++++++ setup.py | 2 + show/main.py | 11 ++ sonic_psu/psu_base.py | 26 +++- 6 files changed, 233 insertions(+), 6 deletions(-) create mode 100644 data/etc/bash_completion.d/psuutil create mode 100644 psuutil/__init__.py create mode 100644 psuutil/main.py diff --git a/data/etc/bash_completion.d/psuutil b/data/etc/bash_completion.d/psuutil new file mode 100644 index 0000000000..b70914f91b --- /dev/null +++ b/data/etc/bash_completion.d/psuutil @@ -0,0 +1,8 @@ +_psuutil_completion() { + COMPREPLY=( $( env COMP_WORDS="${COMP_WORDS[*]}" \ + COMP_CWORD=$COMP_CWORD \ + _PSUUTIL_COMPLETE=complete $1 ) ) + return 0 +} + +complete -F _psuutil_completion -o default psuutil; diff --git a/psuutil/__init__.py b/psuutil/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/psuutil/main.py b/psuutil/main.py new file mode 100644 index 0000000000..fdef031c1a --- /dev/null +++ b/psuutil/main.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python +# +# main.py +# +# Command-line utility for interacting with PSU in SONiC +# + +try: + import sys + import os + import subprocess + import click + import imp + import syslog + import types + import traceback + from tabulate import tabulate +except ImportError as e: + raise ImportError("%s - required module not found" % str(e)) + +VERSION = '1.0' + +SYSLOG_IDENTIFIER = "psuutil" +PLATFORM_SPECIFIC_MODULE_NAME = "psuutil" +PLATFORM_SPECIFIC_CLASS_NAME = "PsuUtil" + +PLATFORM_ROOT_PATH = '/usr/share/sonic/device' +PLATFORM_ROOT_PATH_DOCKER = '/usr/share/sonic/platform' +SONIC_CFGGEN_PATH = '/usr/local/bin/sonic-cfggen' +MINIGRAPH_PATH = '/etc/sonic/minigraph.xml' +HWSKU_KEY = "DEVICE_METADATA['localhost']['hwsku']" +PLATFORM_KEY = 'platform' + +# Global platform-specific psuutil class instance +platform_psuutil = None + + +# ========================== Syslog wrappers ========================== + + +def log_info(msg, also_print_to_console=False): + syslog.openlog(SYSLOG_IDENTIFIER) + syslog.syslog(syslog.LOG_INFO, msg) + syslog.closelog() + + if also_print_to_console: + print msg + + +def log_warning(msg, also_print_to_console=False): + syslog.openlog(SYSLOG_IDENTIFIER) + syslog.syslog(syslog.LOG_WARNING, msg) + syslog.closelog() + + if also_print_to_console: + print msg + + +def log_error(msg, also_print_to_console=False): + syslog.openlog(SYSLOG_IDENTIFIER) + syslog.syslog(syslog.LOG_ERR, msg) + syslog.closelog() + + if also_print_to_console: + print msg + + +# ==================== Methods for initialization ==================== + +# Returns platform and HW SKU +def get_platform_and_hwsku(): + try: + proc = subprocess.Popen([SONIC_CFGGEN_PATH, '-v', PLATFORM_KEY], + stdout=subprocess.PIPE, + shell=False, + stderr=subprocess.STDOUT) + stdout = proc.communicate()[0] + proc.wait() + platform = stdout.rstrip('\n') + + proc = subprocess.Popen([SONIC_CFGGEN_PATH, '-m', MINIGRAPH_PATH, '-v', HWSKU_KEY], + stdout=subprocess.PIPE, + shell=False, + stderr=subprocess.STDOUT) + stdout = proc.communicate()[0] + proc.wait() + hwsku = stdout.rstrip('\n') + except OSError, e: + raise OSError("Cannot detect platform") + + return (platform, hwsku) + + +# Loads platform specific psuutil module from source +def load_platform_psuutil(): + global platform_psuutil + + # Get platform and hwsku + (platform, hwsku) = get_platform_and_hwsku() + + # Load platform module from source + platform_path = '' + if len(platform) != 0: + platform_path = "/".join([PLATFORM_ROOT_PATH, platform]) + else: + platform_path = PLATFORM_ROOT_PATH_DOCKER + hwsku_path = "/".join([platform_path, hwsku]) + + try: + module_file = "/".join([platform_path, "plugins", PLATFORM_SPECIFIC_MODULE_NAME + ".py"]) + module = imp.load_source(PLATFORM_SPECIFIC_MODULE_NAME, module_file) + except IOError, e: + log_error("Failed to load platform module '%s': %s" % (PLATFORM_SPECIFIC_MODULE_NAME, str(e)), True) + return -1 + + try: + platform_psuutil_class = getattr(module, PLATFORM_SPECIFIC_CLASS_NAME) + platform_psuutil = platform_psuutil_class() + except AttributeError, e: + log_error("Failed to instantiate '%s' class: %s" % (PLATFORM_SPECIFIC_CLASS_NAME, str(e)), True) + return -2 + + return 0 + + +# ==================== CLI commands and groups ==================== + + +# This is our main entrypoint - the main 'psuutil' command +@click.group() +def cli(): + """psuutil - Command line utility for providing PSU status""" + + if os.geteuid() != 0: + print "Root privileges are required for this operation" + sys.exit(1) + + # Load platform-specific psuutil class + err = load_platform_psuutil() + if err != 0: + sys.exit(2) + +# 'version' subcommand +@cli.command() +def version(): + """Display version info""" + click.echo("psuutil version {0}".format(VERSION)) + +# 'numpsus' subcommand +@cli.command() +def numpsus(): + "Display the number of supported PSU in the device" + print(str(platform_psuutil.get_num_psus())) + +# 'status' subcommand +@cli.command() +@click.option('-i', '--index', default=-1, type=int, help="the index of PSU") +@click.option('--textonly', is_flag=True, help="show the PSU status in a simple text format instead of table") +def status(index, textonly): + """Display PSU status""" + supported_psu = range(1, platform_psuutil.get_num_psus() + 1) + psu_ids = [] + if (index < 0): + psu_ids = supported_psu + else: + psu_ids = [index] + + header = ['PSU', 'Status'] + status_table = [] + + for psu in psu_ids: + msg = "" + psu_name = "PSU {}".format(psu) + if psu not in supported_psu: + status_table.append([psu_name, "NOT SUPPORTED"]) + continue + presence = platform_psuutil.get_psu_presence(psu) + if presence: + oper_status = platform_psuutil.get_psu_status(psu) + msg = 'OK' if oper_status else "NOT OK" + else: + msg = 'NOT PRESENT' + status_table.append([psu_name, msg]) + + if textonly: + for status in status_table: + print status[0] + ':' + status[1] + else: + print tabulate(status_table, header, tablefmt="grid") + +if __name__ == '__main__': + cli() diff --git a/setup.py b/setup.py index 5a571ae032..2a52164879 100644 --- a/setup.py +++ b/setup.py @@ -24,6 +24,7 @@ def get_test_suite(): 'debug', 'pfcwd', 'sfputil', + 'psuutil', 'show', 'sonic_eeprom', 'sonic_installer', @@ -61,6 +62,7 @@ def get_test_suite(): 'debug = debug.main:cli', 'pfcwd = pfcwd.main:cli', 'sfputil = sfputil.main:cli', + 'psuutil = psuutil.main:cli', 'show = show.main:cli', 'sonic-clear = clear.main:cli', 'sonic_installer = sonic_installer.main:cli', diff --git a/show/main.py b/show/main.py index 5a2399c833..3e735474d8 100755 --- a/show/main.py +++ b/show/main.py @@ -468,6 +468,17 @@ def syseeprom(): """Show system EEPROM information""" run_command("sudo decode-syseeprom") +# 'psustatus' subcommand ("show platform psustatus") +@platform.command() +@click.option('-i', '--index', default=-1, type=int, help="the index of PSU") +def psustatus(index): + """Show PSU status information""" + command = "sudo psuutil status" + + if index >= 0: + command += " -i {}".format(index) + + run_command(command) # # 'logging' command ("show logging") diff --git a/sonic_psu/psu_base.py b/sonic_psu/psu_base.py index fba50b4627..fe5c3974a6 100644 --- a/sonic_psu/psu_base.py +++ b/sonic_psu/psu_base.py @@ -17,19 +17,33 @@ class PsuBase(object): @abc.abstractmethod def get_num_psus(self): """ - Retrieves the number of PSUs available on the device + Retrieves the number of PSUs supported on the device - :return: An integer, the number of PSUs available on the device + :return: An integer, the number of PSUs supported on the device """ return 0 @abc.abstractmethod def get_psu_status(self, index): """ - Retrieves the oprational status of power supply unit (PSU) defined - by index + Retrieves the operational status of power supply unit (PSU) defined + by index 1-based - :param index: An integer, index of the PSU of which to query status - :return: Boolean, True if PSU is operating properly, False if PSU is faulty + :param index: An integer, 1-based index of the PSU of which to query status + :return: Boolean, + - True if PSU is operating properly: PSU is inserted and powered in the device + - False if PSU is faulty: PSU is inserted in the device but not powered """ return False + + @abc.abstractmethod + def get_psu_presence(self, index): + """ + Retrieves the presence status of power supply unit (PSU) defined + by 1-based index + + :param index: An integer, 1-based index of the PSU of which to query status + :return: Boolean, True if PSU is plugged, False if not + """ + return False + From ab0f1b19e12a6120960038b10b10b86899efe202 Mon Sep 17 00:00:00 2001 From: AndriiS Date: Thu, 30 Nov 2017 21:35:54 +0200 Subject: [PATCH 04/32] [PSU] Small changes for correct PSU plugin import (#156) --- sonic_psu/psu_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonic_psu/psu_base.py b/sonic_psu/psu_base.py index fe5c3974a6..2e2204298e 100644 --- a/sonic_psu/psu_base.py +++ b/sonic_psu/psu_base.py @@ -8,7 +8,7 @@ try: import abc -except ImportError, e: +except ImportError as e: raise ImportError (str(e) + " - required module not found") class PsuBase(object): From f57427ef9a89f5cc804235a48ee232ba2f46fccf Mon Sep 17 00:00:00 2001 From: zhenggen-xu Date: Fri, 1 Dec 2017 11:09:19 -0800 Subject: [PATCH 05/32] Use natual sort for 'show acl table' ports (#157) --- acl_loader/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/acl_loader/main.py b/acl_loader/main.py index b17d647a8a..dbe61e3276 100644 --- a/acl_loader/main.py +++ b/acl_loader/main.py @@ -7,6 +7,7 @@ import json import argparse import tabulate +from natsort import natsorted import openconfig_acl import pyangbind.lib.pybindJSON as pybindJSON @@ -384,7 +385,7 @@ def show_table(self, table_name): if not val["ports"]: data.append([key, val["type"], "", val["policy_desc"]]) else: - ports = sorted(val["ports"], ) + ports = natsorted(val["ports"]) data.append([key, val["type"], ports[0], val["policy_desc"]]) if len(ports) > 1: From 835825dc6bdcd76a7dfd2f03cf2b497439cddd83 Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Tue, 5 Dec 2017 11:01:34 -0800 Subject: [PATCH 06/32] [show]: If rotated syslog.1 file exists, concatenate output of syslog.1 and syslog (#159) --- show/main.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/show/main.py b/show/main.py index 3e735474d8..df806923a9 100755 --- a/show/main.py +++ b/show/main.py @@ -493,7 +493,10 @@ def logging(process, lines, follow): if follow: run_command("sudo tail -f /var/log/syslog") else: - command = "sudo cat /var/log/syslog" + if os.path.isfile("/var/log/syslog.1"): + command = "sudo cat /var/log/syslog.1 /var/log/syslog" + else: + command = "sudo cat /var/log/syslog" if process is not None: command += " | grep '{}'".format(process) From b07486883aab299e12562007d35f24f6317dcd88 Mon Sep 17 00:00:00 2001 From: Wataru Ishida Date: Wed, 6 Dec 2017 16:48:12 +0900 Subject: [PATCH 07/32] [config/show] add support for vlan configuration (#151) $ config vlan add 1000 $ config vlan member add 1000 Ethernet1 $ config vlan member add 1000 Ethernet9_1 -u $ config vlan add 2000 $ config vlan member add 2000 Ethernet1 $ config vlan member add 2000 Ethernet9_2 -u $ show vlan config Name VID Member Mode -------- ----- ----------- -------- Vlan1000 1000 Ethernet1 tagged Ethernet9_1 untagged Vlan2000 2000 Ethernet1 tagged Ethernet9_2 untagged $ config vlan member del 1000 Ethernet1 $ config vlan member del 2000 Ethernet $ show vlan config Name VID Member Mode -------- ----- ----------- -------- Vlan1000 1000 Ethernet9_1 untagged Vlan2000 2000 Ethernet9_2 untagged $ config vlan del 1000 $ config vlan del 2000 $ show vlan config Name VID Member Mode ------ ----- -------- ------ Signed-off-by: Wataru Ishida --- config/main.py | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++ show/main.py | 27 ++++++++++++++++ 2 files changed, 115 insertions(+) mode change 100644 => 100755 config/main.py diff --git a/config/main.py b/config/main.py old mode 100644 new mode 100755 index 1e8fa1f30c..7572513a4e --- a/config/main.py +++ b/config/main.py @@ -189,6 +189,94 @@ def load_minigraph(): _restart_services() print "Please note setting loaded from minigraph will be lost after system reboot. To preserve setting, run `config save`." +# +# 'vlan' group +# +@cli.group() +@click.pass_context +@click.option('-s', '--redis-unix-socket-path', help='unix socket path for redis connection') +def vlan(ctx, redis_unix_socket_path): + """VLAN-related configuration tasks""" + kwargs = {} + if redis_unix_socket_path: + kwargs['unix_socket_path'] = redis_unix_socket_path + config_db = ConfigDBConnector(**kwargs) + config_db.connect(wait_for_init=False) + ctx.obj = {'db': config_db} + pass + +@vlan.command('add') +@click.argument('vid', metavar='', required=True, type=int) +@click.pass_context +def add_vlan(ctx, vid): + db = ctx.obj['db'] + vlan = 'Vlan{}'.format(vid) + if len(db.get_entry('VLAN', vlan)) != 0: + print "{} already exists".format(vlan) + raise click.Abort + db.set_entry('VLAN', vlan, {'vlanid': vid}) + +@vlan.command('del') +@click.argument('vid', metavar='', required=True, type=int) +@click.pass_context +def del_vlan(ctx, vid): + db = ctx.obj['db'] + keys = [ (k, v) for k, v in db.get_table('VLAN_MEMBER') if k == 'Vlan{}'.format(vid) ] + for k in keys: + db.set_entry('VLAN_MEMBER', k, None) + db.set_entry('VLAN', 'Vlan{}'.format(vid), None) + +@vlan.group('member') +@click.pass_context +def vlan_member(ctx): + pass + +@vlan_member.command('add') +@click.argument('vid', metavar='', required=True, type=int) +@click.argument('interface_name', metavar='', required=True) +@click.option('-u', '--untagged', is_flag=True) +@click.pass_context +def add_vlan_member(ctx, vid, interface_name, untagged): + db = ctx.obj['db'] + vlan_name = 'Vlan{}'.format(vid) + vlan = db.get_entry('VLAN', vlan_name) + if len(vlan) == 0: + print "{} doesn't exist".format(vlan_name) + raise click.Abort + members = vlan.get('members', []) + if interface_name in members: + print "{} is already a member of {}".format(interface_name, vlan_name) + raise click.Abort + members.append(interface_name) + vlan['members'] = members + db.set_entry('VLAN', vlan_name, vlan) + db.set_entry('VLAN_MEMBER', (vlan_name, interface_name), {'tagging_mode': "untagged" if untagged else "tagged" }) + + +@vlan_member.command('del') +@click.argument('vid', metavar='', required=True, type=int) +@click.argument('interface_name', metavar='', required=True) +@click.pass_context +def del_vlan_member(ctx, vid, interface_name): + db = ctx.obj['db'] + vlan_name = 'Vlan{}'.format(vid) + vlan = db.get_entry('VLAN', vlan_name) + if len(vlan) == 0: + print "{} doesn't exist".format(vlan_name) + raise click.Abort + members = vlan.get('members', []) + if interface_name not in members: + print "{} is not a member of {}".format(interface_name, vlan_name) + raise click.Abort + members.remove(interface_name) + if len(members) == 0: + del vlan['members'] + else: + vlan['members'] = members + db.set_entry('VLAN', vlan_name, vlan) + db.set_entry('VLAN_MEMBER', (vlan_name, interface_name), None) + + # # 'bgp' group # diff --git a/show/main.py b/show/main.py index df806923a9..83f67e0096 100755 --- a/show/main.py +++ b/show/main.py @@ -10,6 +10,7 @@ from click_default_group import DefaultGroup from natsort import natsorted from tabulate import tabulate +from swsssdk import ConfigDBConnector try: # noinspection PyPep8Naming @@ -724,6 +725,32 @@ def id(bridge_name): command="sudo brctl showmacs {}".format(bridge_name) run_command(command) +@vlan.command() +@click.option('-s', '--redis-unix-socket-path', help='unix socket path for redis connection') +def config(redis_unix_socket_path): + kwargs = {} + if redis_unix_socket_path: + kwargs['unix_socket_path'] = redis_unix_socket_path + config_db = ConfigDBConnector(**kwargs) + config_db.connect(wait_for_init=False) + data = config_db.get_table('VLAN') + keys = data.keys() + + def mode(key, data): + info = [] + for m in data.get('members', []): + entry = config_db.get_entry('VLAN_MEMBER', (key, m)) + mode = entry.get('tagging_mode') + if mode == None: + info.append('?') + else: + info.append(mode) + return '\n'.join(info) + + header = ['Name', 'VID', 'Member', 'Mode'] + click.echo(tabulate([ [k, data[k]['vlanid'], '\n'.join(data[k].get('members', [])), mode(k, data[k])] for k in keys ], header)) + + @cli.command('services') def services(): """Show all daemon services""" From 0c0a2f02c32a8cafb3020d8248ea829bbc959d4a Mon Sep 17 00:00:00 2001 From: Rodny Molina Date: Wed, 6 Dec 2017 01:07:33 -0800 Subject: [PATCH 08/32] First code-drop dealing with intf_description and intf_status enhancements (#158) * First code-drop dealing with intf_description and intf_status enhancements I have taken care of the following items as part of this patch. There is another patch coming right after with changes made over a different repo: * Implemented a proper "show interface description" command -- i entirely removed the old one. * Enabled user to query system for a specific interface. * Extended "show interface status" command to do the same as above (query a specific interface). * Rewrote "show interface status" and integrated this code with "show interface description" one to prevent code-duplication and enhance modularity. * Added "speed" attribute as part of "show interface status" command. admin@lnos-x1-a-asw02:~$ show interfaces description Command: intfutil description Interface Oper Admin Alias Description ----------- ------ ------- ------------- ----------------------- Ethernet112 up up fiftyGigE1/9 Special interface no. 1 Ethernet114 down down fiftyGigE1/10 N/A Ethernet116 up up fiftyGigE1/11 Special interface no. 2 Ethernet118 down down fiftyGigE1/12 N/A Ethernet120 up up fiftyGigE1/13 N/A Ethernet122 down down fiftyGigE1/14 N/A Ethernet124 down up fiftyGigE1/15 N/A admin@lnos-x1-a-asw02:~$ show interfaces status | more Command: intfutil status Interface Lanes Speed MTU Alias Oper Admin ----------- ------- ------- ----- ------------- ------ ------- Ethernet112 113,114 50G 9100 fiftyGigE1/9 up up Ethernet114 115,116 N/A 1500 fiftyGigE1/10 down down Ethernet116 117,118 50G 9100 fiftyGigE1/11 up up Ethernet118 119,120 N/A 1500 fiftyGigE1/12 down down Ethernet120 121,122 N/A 9100 fiftyGigE1/13 up up Ethernet122 123,124 N/A 1500 fiftyGigE1/14 down down Ethernet124 125,126 N/A 9100 fiftyGigE1/15 down up * Removing commented line as per review-comments. --- scripts/interface_stat | 58 -------------- scripts/intfutil | 178 +++++++++++++++++++++++++++++++++++++++++ setup.py | 2 +- show/main.py | 25 ++++-- 4 files changed, 197 insertions(+), 66 deletions(-) delete mode 100755 scripts/interface_stat create mode 100755 scripts/intfutil diff --git a/scripts/interface_stat b/scripts/interface_stat deleted file mode 100755 index 434db777d1..0000000000 --- a/scripts/interface_stat +++ /dev/null @@ -1,58 +0,0 @@ -#! /usr/bin/python - -import swsssdk -import sys -import re -from tabulate import tabulate -from natsort import natsorted - -header = ['Iface', 'Lanes', 'Alias', 'Oper', 'Admin', 'MTU'] - - -PORT_STATUS_TABLE_PREFIX = "PORT_TABLE:" -PORT_LANES_STATUS_FIELD = "lanes" -PORT_ALIAS_STATUS_FIELD = "alias" -PORT_OPER_STATUS_FIELD = "oper_status" -PORT_ADMIN_STATUS_FIELD = "admin_status" -PORT_MTU_STATUS_FIELD = "mtu" - -class Intstat(object): - table = [] - - def get_port_status(self, port_name, status_type): - """ - Get the port status - """ - full_table_id = PORT_STATUS_TABLE_PREFIX + port_name - status = self.db.get(self.db.APPL_DB, full_table_id, status_type) - if status is None: - return "N/A" - else: - return status - - def __init__(self): - self.db = swsssdk.SonicV2Connector(host='127.0.0.1') - self.db.connect(self.db.APPL_DB) - a = self.db.keys(self.db.APPL_DB) - - tables = self.db.keys(self.db.APPL_DB, "PORT_TABLE:*") - i = {} - table = [] - key = [] - - for i in tables: - key = re.split(':', i, maxsplit=1)[-1].strip() - if key and key.startswith('Ethernet'): - table.append((key, self.get_port_status(key, PORT_LANES_STATUS_FIELD), self.get_port_status(key, PORT_ALIAS_STATUS_FIELD), self.get_port_status(key, PORT_OPER_STATUS_FIELD), self.get_port_status(key, PORT_ADMIN_STATUS_FIELD), self.get_port_status(key, PORT_MTU_STATUS_FIELD))) - - sorted_table = natsorted(table) - print tabulate(sorted_table, header, tablefmt="simple", stralign='right') - - -def main(): - interface_stat = Intstat() - - sys.exit(0) - -if __name__ == "__main__": - main() diff --git a/scripts/intfutil b/scripts/intfutil new file mode 100755 index 0000000000..33d418437f --- /dev/null +++ b/scripts/intfutil @@ -0,0 +1,178 @@ +#! /usr/bin/python + +import swsssdk +import sys +import re +from tabulate import tabulate +from natsort import natsorted + + + +# ========================== Common interface-utils logic ========================== + + +PORT_STATUS_TABLE_PREFIX = "PORT_TABLE:" +PORT_LANES_STATUS = "lanes" +PORT_ALIAS = "alias" +PORT_OPER_STATUS = "oper_status" +PORT_ADMIN_STATUS = "admin_status" +PORT_SPEED = "speed" +PORT_MTU_STATUS = "mtu" +PORT_DESCRIPTION = "description" + + +def db_connect(): + db = swsssdk.SonicV2Connector(host='127.0.0.1') + if db == None: + return None + + db.connect(db.APPL_DB) + + return db + + +def db_keys_get(db, intf_name): + + if intf_name == None: + db_keys = db.keys(db.APPL_DB, "PORT_TABLE:*") + elif intf_name.startswith('Ethernet'): + db_keys = db.keys(db.APPL_DB, "PORT_TABLE:%s" % intf_name) + else: + return None + + return db_keys + + +def db_port_status_get(db, intf_name, status_type): + """ + Get the port status + """ + + full_table_id = PORT_STATUS_TABLE_PREFIX + intf_name + status = db.get(db.APPL_DB, full_table_id, status_type) + if status is None: + return "N/A" + + if status_type == PORT_SPEED and status != "N/A": + status = '{}G'.format(status[:2] if int(status) < 100000 else status[:3]) + + return status + + +# ========================== interface-status logic ========================== + +header_stat = ['Interface', 'Lanes', 'Speed', 'MTU', 'Alias', 'Oper', 'Admin'] + +class IntfStatus(object): + + def display_intf_status(self, db_keys): + """ + Generate interface-status output + """ + + i = {} + table = [] + key = [] + + # + # Iterate through all the keys and append port's associated state to + # the result table. + # + for i in db_keys: + key = re.split(':', i, maxsplit=1)[-1].strip() + if key and key.startswith('Ethernet'): + table.append((key, + db_port_status_get(self.db, key, PORT_LANES_STATUS), + db_port_status_get(self.db, key, PORT_SPEED), + db_port_status_get(self.db, key, PORT_MTU_STATUS), + db_port_status_get(self.db, key, PORT_ALIAS), + db_port_status_get(self.db, key, PORT_OPER_STATUS), + db_port_status_get(self.db, key, PORT_ADMIN_STATUS))) + + # Sorting and tabulating the result table. + sorted_table = natsorted(table) + print tabulate(sorted_table, header_stat, tablefmt="simple", stralign='right') + + + def __init__(self, intf_name): + + self.db = db_connect() + if self.db == None: + return + + db_keys = db_keys_get(self.db, intf_name) + if db_keys == None: + return + + self.display_intf_status(db_keys) + + +# ========================== interface-description logic ========================== + + +header_desc = ['Interface', 'Oper', 'Admin', 'Alias', 'Description'] + + +class IntfDescription(object): + + def display_intf_description(self, db_keys): + """ + Generate interface-description output + """ + + i = {} + table = [] + key = [] + + # + # Iterate through all the keys and append port's associated state to + # the result table. + # + for i in db_keys: + key = re.split(':', i, maxsplit=1)[-1].strip() + if key and key.startswith('Ethernet'): + table.append((key, + db_port_status_get(self.db, key, PORT_OPER_STATUS), + db_port_status_get(self.db, key, PORT_ADMIN_STATUS), + db_port_status_get(self.db, key, PORT_ALIAS), + db_port_status_get(self.db, key, PORT_DESCRIPTION))) + + # Sorting and tabulating the result table. + sorted_table = natsorted(table) + print tabulate(sorted_table, header_desc, tablefmt="simple", stralign='right') + + def __init__(self, intf_name): + + self.db = db_connect() + if self.db == None: + return + + db_keys = db_keys_get(self.db, intf_name) + if db_keys == None: + return + + self.display_intf_description(db_keys) + + + +def main(args): + if len(args) == 0: + print "No valid arguments provided" + return + + command = args[0] + if command != "status" and command != "description": + print "No valid command provided" + return + + intf_name = args[1] if len(args) == 2 else None + + if command == "status": + interface_stat = IntfStatus(intf_name) + elif command == "description": + interface_desc = IntfDescription(intf_name) + + sys.exit(0) + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/setup.py b/setup.py index 2a52164879..153cbcde06 100644 --- a/setup.py +++ b/setup.py @@ -46,7 +46,7 @@ def get_test_suite(): 'scripts/fast-reboot-dump.py', 'scripts/fdbshow', 'scripts/generate_dump', - 'scripts/interface_stat', + 'scripts/intfutil', 'scripts/lldpshow', 'scripts/port2alias', 'scripts/portstat', diff --git a/show/main.py b/show/main.py index 83f67e0096..e401c156e4 100755 --- a/show/main.py +++ b/show/main.py @@ -206,6 +206,7 @@ def alias(interfacename): click.echo(tabulate(body, header)) + # 'summary' subcommand ("show interfaces summary") @interfaces.command() @click.argument('interfacename', required=False) @@ -240,6 +241,7 @@ def basic(interfacename): run_command(command) + @transceiver.command() @click.argument('interfacename', required=False) def details(interfacename): @@ -252,15 +254,29 @@ def details(interfacename): run_command(command) + @interfaces.command() @click.argument('interfacename', required=False) def description(interfacename): """Show interface status, protocol and description""" if interfacename is not None: - command = "sudo vtysh -c 'show interface {}'".format(interfacename) + command = "intfutil description {}".format(interfacename) + else: + command = "intfutil description" + + run_command(command) + + +@interfaces.command() +@click.argument('interfacename', required=False) +def status(interfacename): + """Show Interface status information""" + + if interfacename is not None: + command = "intfutil status {}".format(interfacename) else: - command = "sudo vtysh -c 'show interface description'" + command = "intfutil status" run_command(command) @@ -291,11 +307,6 @@ def portchannel(): """Show PortChannel information""" run_command("teamshow") -@interfaces.command() -def status(): - """Show Interface status information""" - run_command("interface_stat") - # # 'mac' command ("show mac ...") From ea108f3cac773ed8e71110c7da577fde28e9219b Mon Sep 17 00:00:00 2001 From: Qi Luo Date: Wed, 6 Dec 2017 16:23:26 -0800 Subject: [PATCH 09/32] Build a python3 wheel with sonic_psu (#162) Signed-off-by: Qi Luo --- setup.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 153cbcde06..b4b1ae5401 100644 --- a/setup.py +++ b/setup.py @@ -1,11 +1,17 @@ import glob +import sys from setuptools import setup import unittest +# For python3 wheel, we currently don't need test +# TODO: build python3 wheel of all the dependencies, and remove conditional test def get_test_suite(): - test_loader = unittest.TestLoader() - test_suite = test_loader.discover('sonic-utilities-tests', pattern='*.py') - return test_suite + if sys.version_info >= (3, 0): + return unittest.TestSuite() + else: + test_loader = unittest.TestLoader() + test_suite = test_loader.discover('sonic-utilities-tests', pattern='*.py') + return test_suite setup( name='sonic-utilities', @@ -85,6 +91,7 @@ def get_test_suite(): 'Natural Language :: English', 'Operating System :: POSIX :: Linux', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.6', 'Topic :: Utilities', ], keywords='sonic SONiC utilities command line cli CLI', From cea150e855b466ae2dbcb56958fb70501cd82b49 Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Thu, 7 Dec 2017 10:43:30 -0800 Subject: [PATCH 10/32] Add GitHub pull request and issue templates (#163) --- .github/ISSUE_TEMPLATE.md | 60 ++++++++++++++++++++++++++++++++ .github/PULL_REQUEST_TEMPLATE.md | 25 +++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..8915ca213f --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,60 @@ + + +**Description** + + + +**Steps to reproduce the issue** +1. +2. +3. + +**Describe the results you received** + + +**Describe the results you expected** + + +**Additional information you deem important (e.g. issue happens only occasionally)** + + +**Output of `show version`** + +``` +(paste your output here) +``` + + + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..adaefd2d43 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,25 @@ + + +**- What I did** + +**- How I did it** + +**- How to verify it** + +**- Previous command output (if the output of a command-line utility has changed)** + +**- New command output (if the output of a command-line utility has changed)** + +--> + From 6a9eb9b4e53420419bd0927bf097a93467569ed4 Mon Sep 17 00:00:00 2001 From: AndriiS Date: Thu, 7 Dec 2017 20:46:55 +0200 Subject: [PATCH 11/32] Align PSU CLI output with SONiC CLI output template (#160) * [PSU CLI] Align PSU CLI output with SONiC CLI output template * [PSU CLI] Removed textonly mode because it's unused with new output format --- psuutil/main.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/psuutil/main.py b/psuutil/main.py index fdef031c1a..de612f1461 100644 --- a/psuutil/main.py +++ b/psuutil/main.py @@ -44,7 +44,7 @@ def log_info(msg, also_print_to_console=False): syslog.closelog() if also_print_to_console: - print msg + click.echo(msg) def log_warning(msg, also_print_to_console=False): @@ -53,7 +53,7 @@ def log_warning(msg, also_print_to_console=False): syslog.closelog() if also_print_to_console: - print msg + click.echo(msg) def log_error(msg, also_print_to_console=False): @@ -62,7 +62,7 @@ def log_error(msg, also_print_to_console=False): syslog.closelog() if also_print_to_console: - print msg + click.echo(msg) # ==================== Methods for initialization ==================== @@ -132,7 +132,7 @@ def cli(): """psuutil - Command line utility for providing PSU status""" if os.geteuid() != 0: - print "Root privileges are required for this operation" + click.echo("Root privileges are required for this operation") sys.exit(1) # Load platform-specific psuutil class @@ -150,12 +150,11 @@ def version(): @cli.command() def numpsus(): "Display the number of supported PSU in the device" - print(str(platform_psuutil.get_num_psus())) + click.echo(str(platform_psuutil.get_num_psus())) # 'status' subcommand @cli.command() @click.option('-i', '--index', default=-1, type=int, help="the index of PSU") -@click.option('--textonly', is_flag=True, help="show the PSU status in a simple text format instead of table") def status(index, textonly): """Display PSU status""" supported_psu = range(1, platform_psuutil.get_num_psus() + 1) @@ -172,7 +171,8 @@ def status(index, textonly): msg = "" psu_name = "PSU {}".format(psu) if psu not in supported_psu: - status_table.append([psu_name, "NOT SUPPORTED"]) + click.echo("Error! The {} is not available on the platform.\n" \ + "Number of supported PSU - {}.".format(psu_name, platform_psuutil.get_num_psus())) continue presence = platform_psuutil.get_psu_presence(psu) if presence: @@ -182,11 +182,8 @@ def status(index, textonly): msg = 'NOT PRESENT' status_table.append([psu_name, msg]) - if textonly: - for status in status_table: - print status[0] + ':' + status[1] - else: - print tabulate(status_table, header, tablefmt="grid") + if status_table: + click.echo(tabulate(status_table, header, tablefmt="simple")) if __name__ == '__main__': cli() From cc147b9d0f0a2edd1e89019052755a92840dd5f9 Mon Sep 17 00:00:00 2001 From: AndriiS Date: Fri, 8 Dec 2017 02:29:27 +0200 Subject: [PATCH 12/32] [PSU] Fixes runtime issue with status displaying (#164) --- psuutil/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psuutil/main.py b/psuutil/main.py index de612f1461..5c3f996023 100644 --- a/psuutil/main.py +++ b/psuutil/main.py @@ -155,7 +155,7 @@ def numpsus(): # 'status' subcommand @cli.command() @click.option('-i', '--index', default=-1, type=int, help="the index of PSU") -def status(index, textonly): +def status(index): """Display PSU status""" supported_psu = range(1, platform_psuutil.get_num_psus() + 1) psu_ids = [] From 1c680b457b0a20642e51a8bcd8ef633e04df2758 Mon Sep 17 00:00:00 2001 From: Shuotian Cheng Date: Thu, 7 Dec 2017 23:49:51 -0800 Subject: [PATCH 13/32] [intfutil]: Fix python indentation (#166) Signed-off-by: Shu0T1an ChenG --- scripts/intfutil | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/intfutil b/scripts/intfutil index 33d418437f..6c44a74fdf 100755 --- a/scripts/intfutil +++ b/scripts/intfutil @@ -53,7 +53,7 @@ def db_port_status_get(db, intf_name, status_type): if status is None: return "N/A" - if status_type == PORT_SPEED and status != "N/A": + if status_type == PORT_SPEED and status != "N/A": status = '{}G'.format(status[:2] if int(status) < 100000 else status[:3]) return status From cb8538962815ea82665a4d4b851d50ea0793811d Mon Sep 17 00:00:00 2001 From: Taoyu Li Date: Fri, 8 Dec 2017 11:01:53 -0800 Subject: [PATCH 14/32] Adapt to py-swsssdk refactor of mod_entry and mod_config (#165) --- acl_loader/main.py | 12 ++++++------ config/main.py | 2 +- pfcwd/main.py | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/acl_loader/main.py b/acl_loader/main.py index dbe61e3276..e1b14f1bba 100644 --- a/acl_loader/main.py +++ b/acl_loader/main.py @@ -340,9 +340,9 @@ def full_update(self): :return: """ for key in self.rules_db_info.keys(): - self.configdb.set_entry(self.ACL_RULE, key, None) + self.configdb.mod_entry(self.ACL_RULE, key, None) - self.configdb.set_config({self.ACL_RULE: self.rules_info}) + self.configdb.mod_config({self.ACL_RULE: self.rules_info}) def incremental_update(self): """ @@ -359,15 +359,15 @@ def incremental_update(self): existing_rules = new_rules.intersection(current_rules) for key in removed_rules: - self.configdb.set_entry(self.ACL_RULE, key, None) + self.configdb.mod_entry(self.ACL_RULE, key, None) for key in added_rules: - self.configdb.set_entry(self.ACL_RULE, key, self.rules_info[key]) + self.configdb.mod_entry(self.ACL_RULE, key, self.rules_info[key]) for key in existing_rules: if cmp(self.rules_info[key], self.rules_db_info[key]): - self.configdb.set_entry(self.ACL_RULE, key, None) - self.configdb.set_entry(self.ACL_RULE, key, self.rules_info[key]) + self.configdb.mod_entry(self.ACL_RULE, key, None) + self.configdb.mod_entry(self.ACL_RULE, key, self.rules_info[key]) def show_table(self, table_name): """ diff --git a/config/main.py b/config/main.py index 7572513a4e..854f84c91a 100755 --- a/config/main.py +++ b/config/main.py @@ -65,7 +65,7 @@ def _switch_bgp_session_status_by_addr(ipaddress, status, verbose): click.echo("{} {} BGP session with neighbor {}...".format(verb, status, ipaddress)) config_db = ConfigDBConnector() config_db.connect() - config_db.set_entry('bgp_neighbor', ipaddress, {'admin_status': status}) + config_db.mod_entry('bgp_neighbor', ipaddress, {'admin_status': status}) def _switch_bgp_session_status(ipaddr_or_hostname, status, verbose): """Start up or shut down BGP session by IP address or hostname diff --git a/pfcwd/main.py b/pfcwd/main.py index 288b10e0ad..764ff25288 100644 --- a/pfcwd/main.py +++ b/pfcwd/main.py @@ -127,8 +127,8 @@ def start(action, restoration_time, ports, detection_time): for port in ports: if port not in all_ports: continue - configdb.set_entry("PFC_WD_TABLE", port, None) - configdb.set_entry("PFC_WD_TABLE", port, pfcwd_info) + configdb.mod_entry("PFC_WD_TABLE", port, None) + configdb.mod_entry("PFC_WD_TABLE", port, pfcwd_info) # Stop WD @cli.command() @@ -148,7 +148,7 @@ def stop(ports): for port in ports: if port not in all_ports: continue - configdb.set_entry("PFC_WD_TABLE", port, None) + configdb.mod_entry("PFC_WD_TABLE", port, None) if __name__ == '__main__': cli() From 7209cca4c0639312357682a1f1b712e60b114b15 Mon Sep 17 00:00:00 2001 From: Andriy Moroz Date: Sat, 9 Dec 2017 00:29:44 +0200 Subject: [PATCH 15/32] Add utility to configure ECN WRED parameters (#153) * Add utility to configure ECN WRED parameters Signed-off-by: Andriy Moroz * Fix verbose flag passing thru Signed-off-by: Andriy Moroz * Fixed ignoring parameters which equal 0 Signed-off-by: Andriy Moroz --- config/main.py | 23 +++++++++ scripts/ecnconfig | 121 ++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 1 + show/main.py | 10 ++++ 4 files changed, 155 insertions(+) create mode 100755 scripts/ecnconfig diff --git a/config/main.py b/config/main.py index 854f84c91a..4ea7695d32 100755 --- a/config/main.py +++ b/config/main.py @@ -411,6 +411,29 @@ def incremental(file_name): command = "acl-loader update incremental {}".format(file_name) run_command(command) +# +# 'ecn' command +# +@cli.command() +@click.option('-profile', metavar='', type=str, required=True, help="Profile name") +@click.option('-rmax', metavar='', type=int, help="Set red max threshold") +@click.option('-rmin', metavar='', type=int, help="Set red min threshold") +@click.option('-ymax', metavar='', type=int, help="Set yellow max threshold") +@click.option('-ymin', metavar='', type=int, help="Set yellow min threshold") +@click.option('-gmax', metavar='', type=int, help="Set green max threshold") +@click.option('-gmin', metavar='', type=int, help="Set green min threshold") +@click.option('-v', '--verbose', is_flag=True, help="Enable verbose output") +def ecn(profile, rmax, rmin, ymax, ymin, gmax, gmin, verbose): + """ECN-related configuration tasks""" + command = "ecnconfig -p %s" % profile + if rmax is not None: command += " -rmax %d" % rmax + if rmin is not None: command += " -rmin %d" % rmin + if ymax is not None: command += " -ymax %d" % ymax + if ymin is not None: command += " -ymin %d" % ymin + if gmax is not None: command += " -gmax %d" % gmax + if gmin is not None: command += " -gmin %d" % gmin + if verbose: command += " -vv" + run_command(command, display_cmd=verbose) if __name__ == '__main__': cli() diff --git a/scripts/ecnconfig b/scripts/ecnconfig new file mode 100755 index 0000000000..fa0ec832c5 --- /dev/null +++ b/scripts/ecnconfig @@ -0,0 +1,121 @@ +#!/usr/bin/python +""" +ecnconfig is the utility to show and change ECN configuration + +usage: ecnconfig [-h] [-v] [-l] [-p PROFILE] [-gmin GREEN_MIN] + [-gmax GREEN_MAX] [-ymin YELLOW_MIN] [-ymax YELLOW_MAX] + [-rmin RED_MIN] [-rmax RED_MAX] [-vv] + +optional arguments: + -h --help show this help message and exit + -v --version show program's version number and exit + -vv --verbose verbose output + -l --list show ECN WRED configuration + -p' --profile specify WRED profile name + -gmin --green-min set min threshold for packets marked green + -gmax --green-max set max threshold for packets marked green + -ymin --yellow-min set min threshold for packets marked yellow + -ymax --yellow-max set max threshold for packets marked yellow + -rmin --red-min set min threshold for packets marked red + -rmax --red-max set max threshold for packets marked red +""" +from __future__ import print_function + +import os +import sys +import json +import argparse +import swsssdk +from tabulate import tabulate + +WRED_PROFILE_TABLE_NAME = "WRED_PROFILE" +WRED_CONFIG_FIELDS = { + "gmax": "green_max_threshold", + "gmin": "green_min_threshold", + "ymax": "yellow_max_threshold", + "ymin": "yellow_min_threshold", + "rmax": "red_max_threshold", + "rmin": "red_min_threshold" +} + +class EcnConfig(object): + """ + Process aclstat + """ + def __init__(self, verbose): + self.ports = [] + self.queues = [] + self.verbose = verbose + + # Set up db connections + self.db = swsssdk.ConfigDBConnector() + self.db.connect() + + def list(self): + wred_tables = self.db.get_table(WRED_PROFILE_TABLE_NAME) + for table in wred_tables: + profile_name = table + profile_data = wred_tables[table] + config = [] + print("Profile: " + profile_name) + for field in profile_data: + line = [field, profile_data[field]] + config.append(line) + print(tabulate(config) + "\n") + if self.verbose: + print("Total profiles: %d" % len(wred_tables)) + + def set_wred_threshold(self, profile, threshold, value): + field = WRED_CONFIG_FIELDS[threshold] + if self.verbose: + print("Setting %s value to %s" % (field, value)) + self.db.set_entry(WRED_PROFILE_TABLE_NAME, profile, {field: value}) + +def main(): + parser = argparse.ArgumentParser(description='Show and change ECN WRED configuration', + version='1.0.0', + formatter_class=argparse.RawTextHelpFormatter) + # group = parser.add_mutually_exclusive_group() + parser.add_argument('-l', '--list', action='store_true', help='show ECN WRED configuration') + parser.add_argument('-p', '--profile', type=str, help='specify WRED profile name', default=None) + parser.add_argument('-gmin', '--green-min', type=str, help='set min threshold for packets marked \'green\'', default=None) + parser.add_argument('-gmax', '--green-max', type=str, help='set max threshold for packets marked \'green\'', default=None) + parser.add_argument('-ymin', '--yellow-min', type=str, help='set min threshold for packets marked \'yellow\'', default=None) + parser.add_argument('-ymax', '--yellow-max', type=str, help='set max threshold for packets marked \'yellow\'', default=None) + parser.add_argument('-rmin', '--red-min', type=str, help='set min threshold for packets marked \'red\'', default=None) + parser.add_argument('-rmax', '--red-max', type=str, help='set max threshold for packets marked \'red\'', default=None) + parser.add_argument('-vv', '--verbose', action='store_true', help='Verbose output', default=False) + args = parser.parse_args() + + try: + ecn = EcnConfig(args.verbose) + if args.list: + if len(sys.argv) > (3 if args.verbose else 2): + raise Exception("Input arguments error. No set options allowed when -l[ist] specified") + ecn.list() + elif args.profile: + if len(sys.argv) < (5 if args.verbose else 4): + raise Exception("Input arguments error. Specify at least one threshold parameter to set") + # the following parameters can be combined in one run + if args.green_max: + ecn.set_wred_threshold(args.profile, "gmax", args.green_max) + if args.green_min: + ecn.set_wred_threshold(args.profile, "gmin", args.green_min) + if args.yellow_max: + ecn.set_wred_threshold(args.profile, "ymax", args.yellow_max) + if args.yellow_min: + ecn.set_wred_threshold(args.profile, "ymin", args.yellow_min) + if args.red_max: + ecn.set_wred_threshold(args.profile, "rmax", args.red_max) + if args.red_min: + ecn.set_wred_threshold(args.profile, "rmin", args.red_min) + else: + parser.print_help() + sys.exit(1) + + except Exception as e: + print(e.message, file=sys.stderr) + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/setup.py b/setup.py index b4b1ae5401..ecbaf52598 100644 --- a/setup.py +++ b/setup.py @@ -48,6 +48,7 @@ def get_test_suite(): 'scripts/coredump-compress', 'scripts/decode-syseeprom', 'scripts/dropcheck', + 'scripts/ecnconfig', 'scripts/fast-reboot', 'scripts/fast-reboot-dump.py', 'scripts/fdbshow', diff --git a/show/main.py b/show/main.py index e401c156e4..b5a569c9c5 100755 --- a/show/main.py +++ b/show/main.py @@ -833,5 +833,15 @@ def rule(table_name, rule_id): run_command("acl-loader show rule {} {}".format(table_name, rule_id)) +# +# 'session' command (show ecn) +# +@cli.command('ecn') +def ecn(): + """Show ECN configuration""" + command = "ecnconfig -l" + proc = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True) + click.echo(proc.stdout.read()) + if __name__ == '__main__': cli() From 49a86db7ff181b9e132676f56fdf94ec068197e1 Mon Sep 17 00:00:00 2001 From: Shuotian Cheng Date: Mon, 11 Dec 2017 17:12:02 -0800 Subject: [PATCH 16/32] [psuutil]: Shorten the help message to display it fully (#168) Signed-off-by: Shu0T1an ChenG --- psuutil/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psuutil/main.py b/psuutil/main.py index 5c3f996023..002619e0b4 100644 --- a/psuutil/main.py +++ b/psuutil/main.py @@ -149,7 +149,7 @@ def version(): # 'numpsus' subcommand @cli.command() def numpsus(): - "Display the number of supported PSU in the device" + """Display number of supported PSUs on device""" click.echo(str(platform_psuutil.get_num_psus())) # 'status' subcommand From be91f161b290328773b98f1e21f7feee797ce91c Mon Sep 17 00:00:00 2001 From: Andriy Moroz Date: Tue, 12 Dec 2017 10:39:24 +0200 Subject: [PATCH 17/32] [ecnconfig] Fix db access from set to mod (#169) Signed-off-by: Andriy Moroz --- scripts/ecnconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ecnconfig b/scripts/ecnconfig index fa0ec832c5..3fa3124ad6 100755 --- a/scripts/ecnconfig +++ b/scripts/ecnconfig @@ -69,7 +69,7 @@ class EcnConfig(object): field = WRED_CONFIG_FIELDS[threshold] if self.verbose: print("Setting %s value to %s" % (field, value)) - self.db.set_entry(WRED_PROFILE_TABLE_NAME, profile, {field: value}) + self.db.mod_entry(WRED_PROFILE_TABLE_NAME, profile, {field: value}) def main(): parser = argparse.ArgumentParser(description='Show and change ECN WRED configuration', From 0fdd9f9e8a8967a58afc12a71405a7ef139e1955 Mon Sep 17 00:00:00 2001 From: Liuqu Date: Thu, 14 Dec 2017 14:02:45 +0800 Subject: [PATCH 18/32] [TACACS+]: Add configuration support for TACACS+ (#125) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [TACACS+]: Add configuration support for TACACS+ * Add config and show commands for TACACS+ * Add hostcfgd to listen configDB for TACACS+ and AAA, modify the pam configuration for Authentication in host. Signed-off-by: chenchen.qcc@alibaba-inc.com * [TACACS+]: Update config command * Add help comments for TACACS+ command * Use 'default' command to recover TACACS+ configuration Signed-off-by: chenchen.qcc@alibaba-inc.com * [TACACS+]: Adapt to the change for set_entry in ConfigDBConnector * The method set_entry in class ConfigDBConnector has changed to update all column key-value tuples. Modify the config command to adapt to this API change. Signed-off-by: Chenchen Qi * [TACACS+]: Move hostcfgd to sonic-buildimage * Command list config aaa authentication login [{tacacs+, local} | default] config aaa authentication failthrough [enable | disable | default] config tacacs passkey config tacacs authtype [pap | chap | mschap] config tacacs timeout <0-60> config tacacs add --port <1–65535> --timeout <1–60> --key --type [pap | chap | mschap] --pri <1-64> config tacacs delete show aaa show tacacs Signed-off-by: Chenchen Qi * [TACACS+]: Replace set_entry with mod_entry * Replace set_entry with mod_entry when modify the specific key-value pair in configdb. Signed-off-by: Chenchen Qi * [TACACS+]: Add default value print for TACACS+ show command Signed-off-by: Chenchen Qi --- config/aaa.py | 208 +++++++++++++++++++++++++++++++++++++++++++++++++ config/main.py | 4 + setup.py | 2 +- show/main.py | 52 +++++++++++++ 4 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 config/aaa.py diff --git a/config/aaa.py b/config/aaa.py new file mode 100644 index 0000000000..dbab270f10 --- /dev/null +++ b/config/aaa.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python -u +# -*- coding: utf-8 -*- + +import click +import netaddr +from swsssdk import ConfigDBConnector + + +def is_ipaddress(val): + if not val: + return False + try: + netaddr.IPAddress(str(val)) + except: + return False + return True + + +def add_table_kv(table, entry, key, val): + config_db = ConfigDBConnector() + config_db.connect() + config_db.mod_entry(table, entry, {key:val}) + + +def del_table_key(table, entry, key): + config_db = ConfigDBConnector() + config_db.connect() + data = config_db.get_entry(table, entry) + if data: + if key in data: + del data[key] + config_db.set_entry(table, entry, data) + + +@click.group() +def aaa(): + """AAA command line""" + pass + + +# cmd: aaa authentication +@click.group() +def authentication(): + """User authentication""" + pass +aaa.add_command(authentication) + + +# cmd: aaa authentication failthrough +@click.command() +@click.argument('option', type=click.Choice(["enable", "disable", "default"])) +def failthrough(option): + """Allow AAA fail-through [enable | disable | default]""" + if option == 'default': + del_table_key('AAA', 'authentication', 'failthrough') + else: + if option == 'enable': + add_table_kv('AAA', 'authentication', 'failthrough', True) + elif option == 'disable': + add_table_kv('AAA', 'authentication', 'failthrough', False) +authentication.add_command(failthrough) + + +# cmd: aaa authentication fallback +@click.command() +@click.argument('option', type=click.Choice(["enable", "disable", "default"])) +def fallback(option): + """Allow AAA fallback [enable | disable | default]""" + if option == 'default': + del_table_key('AAA', 'authentication', 'fallback') + else: + if option == 'enable': + add_table_kv('AAA', 'authentication', 'fallback', True) + elif option == 'disable': + add_table_kv('AAA', 'authentication', 'fallback', False) +authentication.add_command(fallback) + + +@click.command() +@click.argument('auth_protocol', nargs=-1, type=click.Choice(["tacacs+", "local", "default"])) +def login(auth_protocol): + """Switch login authentication [ {tacacs+, local} | default ]""" + if len(auth_protocol) is 0: + print 'Not support empty argument' + return + + if 'default' in auth_protocol: + del_table_key('AAA', 'authentication', 'login') + else: + val = auth_protocol[0] + if len(auth_protocol) == 2: + val += ',' + auth_protocol[1] + add_table_kv('AAA', 'authentication', 'login', val) +authentication.add_command(login) + + +@click.group() +def tacacs(): + """TACACS+ server configuration""" + pass + + +@click.group() +@click.pass_context +def default(ctx): + """set its default configuration""" + ctx.obj = 'default' +tacacs.add_command(default) + + +@click.command() +@click.argument('second', metavar='', type=click.IntRange(0, 60), required=False) +@click.pass_context +def timeout(ctx, second): + """Specify TACACS+ server global timeout <0 - 60>""" + if ctx.obj == 'default': + del_table_key('TACPLUS', 'global', 'timeout') + elif second: + add_table_kv('TACPLUS', 'global', 'timeout', second) + else: + click.echo('Not support empty argument') +tacacs.add_command(timeout) +default.add_command(timeout) + + +@click.command() +@click.argument('type', metavar='', type=click.Choice(["chap", "pap", "mschap"]), required=False) +@click.pass_context +def authtype(ctx, type): + """Specify TACACS+ server global auth_type [chap | pap | mschap]""" + if ctx.obj == 'default': + del_table_key('TACPLUS', 'global', 'auth_type') + elif type: + add_table_kv('TACPLUS', 'global', 'auth_type', type) + else: + click.echo('Not support empty argument') +tacacs.add_command(authtype) +default.add_command(authtype) + + +@click.command() +@click.argument('secret', metavar='', required=False) +@click.pass_context +def passkey(ctx, secret): + """Specify TACACS+ server global passkey """ + if ctx.obj == 'default': + del_table_key('TACPLUS', 'global', 'passkey') + elif secret: + add_table_kv('TACPLUS', 'global', 'passkey', secret) + else: + click.echo('Not support empty argument') +tacacs.add_command(passkey) +default.add_command(passkey) + + +# cmd: tacacs add --timeout SECOND --key SECRET --type TYPE --port PORT --pri PRIORITY +@click.command() +@click.argument('address', metavar='') +@click.option('-t', '--timeout', help='Transmission timeout interval, default 5', type=int) +@click.option('-k', '--key', help='Shared secret') +@click.option('-a', '--auth_type', help='Authentication type, default pap', type=click.Choice(["chap", "pap", "mschap"])) +@click.option('-o', '--port', help='TCP port range is 1 to 65535, default 49', type=click.IntRange(1, 65535), default=49) +@click.option('-p', '--pri', help="Priority, default 1", type=click.IntRange(1, 64), default=1) +def add(address, timeout, key, auth_type, port, pri): + """Specify a TACACS+ server""" + if not is_ipaddress(address): + click.echo('Invalid ip address') + return + + config_db = ConfigDBConnector() + config_db.connect() + old_data = config_db.get_entry('TACPLUS_SERVER', address) + if old_data != {}: + click.echo('server %s already exists' % address) + else: + data = { + 'tcp_port': str(port), + 'priority': pri + } + if auth_type is not None: + data['auth_type'] = auth_type + if timeout is not None: + data['timeout'] = str(timeout) + if key is not None: + data['passkey'] = key + config_db.set_entry('TACPLUS_SERVER', address, data) +tacacs.add_command(add) + + +# cmd: tacacs delete +# 'del' is keyword, replace with 'delete' +@click.command() +@click.argument('address', metavar='') +def delete(address): + """Delete a TACACS+ server""" + if not is_ipaddress(address): + click.echo('Invalid ip address') + return + + config_db = ConfigDBConnector() + config_db.connect() + config_db.set_entry('TACPLUS_SERVER', address, None) +tacacs.add_command(delete) + + +if __name__ == "__main__": + aaa() + diff --git a/config/main.py b/config/main.py index 4ea7695d32..c101a445a1 100755 --- a/config/main.py +++ b/config/main.py @@ -9,6 +9,8 @@ from swsssdk import ConfigDBConnector from minigraph import parse_device_desc_xml +import aaa + SONIC_CFGGEN_PATH = "sonic-cfggen" MINIGRAPH_PATH = "/etc/sonic/minigraph.xml" MINIGRAPH_BGP_SESSIONS = "minigraph_bgp" @@ -112,6 +114,8 @@ def cli(): """SONiC command line - 'config' command""" if os.geteuid() != 0: exit("Root privileges are required for this operation") +cli.add_command(aaa.aaa) +cli.add_command(aaa.tacacs) @cli.command() @click.option('-y', '--yes', is_flag=True, callback=_abort_if_false, diff --git a/setup.py b/setup.py index ecbaf52598..6e5e524413 100644 --- a/setup.py +++ b/setup.py @@ -57,7 +57,7 @@ def get_test_suite(): 'scripts/lldpshow', 'scripts/port2alias', 'scripts/portstat', - 'scripts/teamshow', + 'scripts/teamshow' ], data_files=[ ('/etc/bash_completion.d', glob.glob('data/etc/bash_completion.d/*')), diff --git a/show/main.py b/show/main.py index b5a569c9c5..ff302627a9 100755 --- a/show/main.py +++ b/show/main.py @@ -778,6 +778,58 @@ def services(): else: break +@cli.command() +def aaa(): + """Show AAA configuration in ConfigDb""" + config_db = ConfigDBConnector() + config_db.connect() + data = config_db.get_table('AAA') + output = '' + + aaa = { + 'authentication': { + 'login': 'local (default)', + 'failthrough': 'True (default)', + 'fallback': 'True (default)' + } + } + aaa['authentication'].update(data['authentication']) + for row in aaa: + entry = aaa[row] + for key in entry: + output += ('AAA %s %s %s\n' % (row, key, str(entry[key]))) + click.echo(output) + + +@cli.command() +def tacacs(): + """Show TACACS+ configuration""" + config_db = ConfigDBConnector() + config_db.connect() + output = '' + data = config_db.get_table('TACPLUS') + + tacplus = { + 'global': { + 'auth_type': 'pap (default)', + 'timeout': '5 (default)', + 'passkey': ' (default)' + } + } + tacplus['global'].update(data['global']) + for key in tacplus['global']: + output += ('TACPLUS global %s %s\n' % (str(key), str(tacplus['global'][key]))) + + data = config_db.get_table('TACPLUS_SERVER') + if data != {}: + for row in data: + entry = data[row] + output += ('\nTACPLUS_SERVER address %s\n' % row) + for key in entry: + output += (' %s %s\n' % (key, str(entry[key]))) + click.echo(output) + + # # 'session' command ### # From 8677d5f457c9a20f0fac2a4fdde569a8338c77e3 Mon Sep 17 00:00:00 2001 From: Marian Pritsak Date: Thu, 14 Dec 2017 08:07:00 +0200 Subject: [PATCH 19/32] [pfcwd]: Refine 'show stats' output (#170) Group columns in OK/DROP pairs Add option to show empty lines now hidden by default Signed-off-by: marian-pritsak --- pfcwd/main.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/pfcwd/main.py b/pfcwd/main.py index 764ff25288..eb95e17fce 100644 --- a/pfcwd/main.py +++ b/pfcwd/main.py @@ -6,16 +6,11 @@ from natsort import natsorted STATS_DESCRIPTION = [ - ('STORM DETECTED CNT', 'PFC_WD_QUEUE_STATS_DEADLOCK_DETECTED'), - ('STORM RESTORED CNT', 'PFC_WD_QUEUE_STATS_DEADLOCK_RESTORED'), - ('TX PACKETS', 'PFC_WD_QUEUE_STATS_TX_PACKETS'), - ('RX PACKETS', 'PFC_WD_QUEUE_STATS_TX_DROPPED_PACKETS'), - ('TX PACKETS DROP', 'PFC_WD_QUEUE_STATS_RX_PACKETS'), - ('RX PACKETS DROP', 'PFC_WD_QUEUE_STATS_RX_DROPPED_PACKETS_LAST'), - ('TX PACKETS LAST', 'PFC_WD_QUEUE_STATS_TX_PACKETS_LAST'), - ('RX PACKETS LAST', 'PFC_WD_QUEUE_STATS_TX_DROPPED_PACKETS_LAST'), - ('TX PACKETS LAST DROP', 'PFC_WD_QUEUE_STATS_RX_PACKETS_LAST'), - ('RX PACKETS LAST DROP', 'PFC_WD_QUEUE_STATS_RX_DROPPED_PACKETS_LAST') + ('STORM DETECTED/RESTORED', 'PFC_WD_QUEUE_STATS_DEADLOCK_DETECTED', 'PFC_WD_QUEUE_STATS_DEADLOCK_RESTORED'), + ('TX OK/DROP', 'PFC_WD_QUEUE_STATS_TX_PACKETS', 'PFC_WD_QUEUE_STATS_TX_DROPPED_PACKETS'), + ('RX OK/DROP', 'PFC_WD_QUEUE_STATS_RX_PACKETS', 'PFC_WD_QUEUE_STATS_RX_DROPPED_PACKETS'), + ('TX LAST OK/DROP', 'PFC_WD_QUEUE_STATS_TX_PACKETS_LAST', 'PFC_WD_QUEUE_STATS_TX_DROPPED_PACKETS_LAST'), + ('RX LAST OK/DROP', 'PFC_WD_QUEUE_STATS_RX_PACKETS_LAST', 'PFC_WD_QUEUE_STATS_RX_DROPPED_PACKETS_LAST'), ] CONFIG_DESCRIPTION = [ @@ -47,8 +42,9 @@ def show(): # Show stats @show.command() +@click.option('-e', '--empty', is_flag = True) @click.argument('queues', nargs = -1) -def stats(queues): +def stats(empty, queues): """ Show PFC Watchdog stats per queue """ db = swsssdk.SonicV2Connector(host='127.0.0.1') db.connect(db.COUNTERS_DB) @@ -64,9 +60,10 @@ def stats(queues): if stats is None: continue for stat in STATS_DESCRIPTION: - line = stats.get(stat[1], '0') + line = stats.get(stat[1], '0') + '/' + stats.get(stat[2], '0') stats_list.append(line) - table.append([queue] + stats_list) + if stats_list != ['0/0'] * len(STATS_DESCRIPTION) or empty: + table.append([queue] + stats_list) click.echo(tabulate(table, STATS_HEADER, stralign='right', numalign='right', tablefmt='simple')) From 99c3f48247be1c683b9890759191e80e65896017 Mon Sep 17 00:00:00 2001 From: Marian Pritsak Date: Tue, 19 Dec 2017 21:13:36 +0200 Subject: [PATCH 20/32] [acl_loader]: Add status column to session table (#177) Signed-off-by: marian-pritsak --- acl_loader/main.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/acl_loader/main.py b/acl_loader/main.py index e1b14f1bba..7b35f26a8c 100644 --- a/acl_loader/main.py +++ b/acl_loader/main.py @@ -12,6 +12,7 @@ import openconfig_acl import pyangbind.lib.pybindJSON as pybindJSON from swsssdk import ConfigDBConnector +from swsssdk import SonicV2Connector def info(msg): @@ -81,6 +82,8 @@ def __init__(self): self.sessions_db_info = {} self.configdb = ConfigDBConnector() self.configdb.connect() + self.appdb = SonicV2Connector() + self.appdb.connect(self.appdb.APPL_DB) self.read_tables_info() self.read_rules_info() @@ -112,6 +115,11 @@ def read_sessions_info(self): :return: """ self.sessions_db_info = self.configdb.get_table(self.MIRROR_SESSION) + for key in self.sessions_db_info.keys(): + app_db_info = self.appdb.get_all(self.appdb.APPL_DB, "{}:{}".format(self.MIRROR_SESSION, key)) + + status = app_db_info.get("status", "inactive") + self.sessions_db_info[key]["status"] = status def get_sessions_db_info(self): """ @@ -400,14 +408,14 @@ def show_session(self, session_name): :param session_name: Optional. Mirror session name. Filter sessions by specified name. :return: """ - header = ("Name", "SRC IP", "DST IP", "GRE", "DSCP", "TTL", "Queue") + header = ("Name", "Status", "SRC IP", "DST IP", "GRE", "DSCP", "TTL", "Queue") data = [] for key, val in self.get_sessions_db_info().iteritems(): if session_name and key != session_name: continue - data.append([key, val["src_ip"], val["dst_ip"], + data.append([key, val["status"], val["src_ip"], val["dst_ip"], val.get("gre_type", ""), val.get("dscp", ""), val.get("ttl", ""), val.get("queue", "")]) From e9a66976c1d32b903a484ecc0fc5c384edd73719 Mon Sep 17 00:00:00 2001 From: zhenggen-xu Date: Wed, 20 Dec 2017 10:00:38 -0800 Subject: [PATCH 21/32] fix aclshow -d not showing ACL table issue (#175) --- scripts/aclshow | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/scripts/aclshow b/scripts/aclshow index 18a02db850..b9c4777105 100755 --- a/scripts/aclshow +++ b/scripts/aclshow @@ -220,6 +220,9 @@ class AclStat(object): def adj_len(text, mlen): return text + "." * (mlen - len(text)) + if not self.acl_tables: + return + # determine max len of the table and rule properties onetable = self.acl_tables.itervalues().next() tlen = len(onetable.keys()[0]) @@ -227,11 +230,14 @@ class AclStat(object): if len(property) > tlen: tlen = len(property) - onerule = self.acl_rules.itervalues().next() - rlen = len(onerule.keys()[0]) - for property in onerule.keys() + [PACKETS_COUNTER, BYTES_COUNTER]: - if len(property) > rlen: - rlen = len(property) + if not self.acl_rules: + rlen = 0 + else: + onerule = self.acl_rules.itervalues().next() + rlen = len(onerule.keys()[0]) + for property in onerule.keys() + [PACKETS_COUNTER, BYTES_COUNTER]: + if len(property) > rlen: + rlen = len(property) mlen = max(rlen, tlen) + 1 From 67102194811c2ea417d7a559d5a63328b47801db Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Wed, 20 Dec 2017 11:42:50 -0800 Subject: [PATCH 22/32] [acl-loader]: Add support for handling control plane ACLs (#172) * Also change 'Table name' to 'Rule name' in header of show_rule() output * Also remove unused imports * Also add ACL_TABLE_TYPE_MIRROR constant, other small tweaks, comments and docstrings --- acl_loader/main.py | 53 ++++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/acl_loader/main.py b/acl_loader/main.py index 7b35f26a8c..a06cb924f8 100644 --- a/acl_loader/main.py +++ b/acl_loader/main.py @@ -1,11 +1,7 @@ - #!/usr/bin/env python import click -import sys -import os.path import json -import argparse import tabulate from natsort import natsorted @@ -45,6 +41,8 @@ class AclLoader(object): ACL_TABLE = "ACL_TABLE" ACL_RULE = "ACL_RULE" + ACL_TABLE_TYPE_MIRROR = "MIRROR" + ACL_TABLE_TYPE_CTRLPLANE = "CTRLPLANE" MIRROR_SESSION = "MIRROR_SESSION" SESSION_PREFIX = "everflow" @@ -165,11 +163,19 @@ def is_table_valid(self, tname): def is_table_mirror(self, tname): """ - Check if ACL table type is MIRROR + Check if ACL table type is ACL_TABLE_TYPE_MIRROR :param tname: ACL table name - :return: True if table type is MIRROR else False + :return: True if table type is ACL_TABLE_TYPE_MIRROR else False """ - return self.tables_db_info[tname]['type'].upper() == "MIRROR" + return self.tables_db_info[tname]['type'].upper() == self.ACL_TABLE_TYPE_MIRROR + + def is_table_control_plane(self, tname): + """ + Check if ACL table type is ACL_TABLE_TYPE_CTRLPLANE + :param tname: ACL table name + :return: True if table type is ACL_TABLE_TYPE_CTRLPLANE else False + """ + return self.tables_db_info[tname]['type'].upper() == self.ACL_TABLE_TYPE_CTRLPLANE def load_rules_from_file(self, filename): """ @@ -185,7 +191,9 @@ def convert_action(self, table_name, rule_idx, rule): rule_props = {} if rule.actions.config.forwarding_action == "ACCEPT": - if self.is_table_mirror(table_name): + if self.is_table_control_plane(table_name): + rule_props["PACKET_ACTION"] = "ACCEPT" + elif self.is_table_mirror(table_name): session_name = self.get_session_name() if not session_name: raise AclLoaderException("Mirroring session does not exist") @@ -247,6 +255,15 @@ def convert_ipv4(self, table_name, rule_idx, rule): return rule_props def convert_port(self, port): + """ + Convert port field format from openconfig ACL to Config DB schema + :param port: String, ACL port number or range in openconfig format + :return: Tuple, first value is converted port string, + second value is boolean, True if value is a port range, False + if it is a single port value + """ + # OpenConfig port range is of the format "####..####", whereas + # Config DB format is "####-####" if ".." in port: return port.replace("..", "-"), True else: @@ -266,21 +283,21 @@ def convert_transport(self, table_name, rule_idx, rule): for flag in rule.transport.config.tcp_flags: if flag == "TCP_FIN": - tcp_flags = tcp_flags | 0x01 + tcp_flags |= 0x01 if flag == "TCP_SYN": - tcp_flags = tcp_flags | 0x02 + tcp_flags |= 0x02 if flag == "TCP_RST": - tcp_flags = tcp_flags | 0x04 + tcp_flags |= 0x04 if flag == "TCP_PSH": - tcp_flags = tcp_flags | 0x08 + tcp_flags |= 0x08 if flag == "TCP_ACK": - tcp_flags = tcp_flags | 0x10 + tcp_flags |= 0x10 if flag == "TCP_URG": - tcp_flags = tcp_flags | 0x20 + tcp_flags |= 0x20 if flag == "TCP_ECE": - tcp_flags = tcp_flags | 0x40 + tcp_flags |= 0x40 if flag == "TCP_CWR": - tcp_flags = tcp_flags | 0x80 + tcp_flags |= 0x80 if tcp_flags: rule_props["TCP_FLAGS"] = '0x{:02x}/0x{:02x}'.format(tcp_flags, tcp_flags) @@ -316,7 +333,7 @@ def deny_rule(self, table_name): rule_props = {} rule_data = {(table_name, "DEFAULT_RULE"): rule_props} rule_props["PRIORITY"] = self.min_priority - rule_props["ETHER_TYPE"] = "0x0800" + rule_props["ETHER_TYPE"] = self.ethertype_map["ETHERTYPE_IPV4"] rule_props["PACKET_ACTION"] = "DROP" return rule_data @@ -428,7 +445,7 @@ def show_rule(self, table_name, rule_id): :param rule_id: Optional. ACL rule name. Filter rule by specified rule name. :return: """ - header = ("Rule ID", "Table Name", "Priority", "Action", "Match") + header = ("Rule ID", "Rule Name", "Priority", "Action", "Match") ignore_list = ["PRIORITY", "PACKET_ACTION", "MIRROR_ACTION"] From 24ca57713af367aba11b7cd726dd0da40e1a511c Mon Sep 17 00:00:00 2001 From: pavel-shirshov Date: Wed, 20 Dec 2017 12:53:10 -0800 Subject: [PATCH 23/32] [Fast-Reboot]: Add directed GARP (#178) * Simplify couple functions * Use vlan and mac address to check that arp entry has corresponding fdb entry * Send directed GARP right before fast-reload procedure --- scripts/fast-reboot-dump.py | 128 ++++++++++++++++++++++++++---------- 1 file changed, 92 insertions(+), 36 deletions(-) diff --git a/scripts/fast-reboot-dump.py b/scripts/fast-reboot-dump.py index c04d2b8801..910851d9b2 100644 --- a/scripts/fast-reboot-dump.py +++ b/scripts/fast-reboot-dump.py @@ -1,28 +1,36 @@ #!/usr/bin/env python - import swsssdk import json -from pprint import pprint +import socket +import struct +from fcntl import ioctl +import binascii + +ARP_CHUNK = binascii.unhexlify('08060001080006040001') # defines a part of the packet for ARP Request +ARP_PAD = binascii.unhexlify('00' * 18) def generate_arp_entries(filename, all_available_macs): db = swsssdk.SonicV2Connector() db.connect(db.APPL_DB, False) # Make one attempt only arp_output = [] - + arp_entries = [] keys = db.keys(db.APPL_DB, 'NEIGH_TABLE:*') keys = [] if keys is None else keys for key in keys: + vlan_name = key.split(':')[1] + ip_addr = key.split(':')[2] entry = db.get_all(db.APPL_DB, key) - if entry['neigh'].lower() not in all_available_macs: - # print me to log + if (vlan_name, entry['neigh'].lower()) not in all_available_macs: + # FIXME: print me to log continue obj = { key: entry, 'OP': 'SET' } + arp_entries.append((vlan_name, entry['neigh'].lower(), ip_addr)) arp_output.append(obj) db.close(db.APPL_DB) @@ -30,26 +38,16 @@ def generate_arp_entries(filename, all_available_macs): with open(filename, 'w') as fp: json.dump(arp_output, fp, indent=2, separators=(',', ': ')) - return + return arp_entries def is_mac_unicast(mac): first_octet = mac.split(':')[0] - - if int(first_octet, 16) & 0x01 == 0: - return True - else: - return False + return int(first_octet, 16) & 0x01 == 0 def get_vlan_ifaces(): vlans = [] with open('/proc/net/dev') as fp: - raw = fp.read() - - for line in raw.split('\n'): - if 'Vlan' not in line: - continue - vlan_name = line.split(':')[0].strip() - vlans.append(vlan_name) + vlans = [line.split(':')[0].strip() for line in fp if 'Vlan' in line] return vlans @@ -95,15 +93,15 @@ def get_map_bridge_port_id_2_iface_name(db): return bridge_port_id_2_iface_name -def get_fdb(db, vlan_id, bridge_id_2_iface): +def get_fdb(db, vlan_name, vlan_id, bridge_id_2_iface): fdb_types = { 'SAI_FDB_ENTRY_TYPE_DYNAMIC': 'dynamic', 'SAI_FDB_ENTRY_TYPE_STATIC' : 'static' } available_macs = set() - - entries = [] + map_mac_ip = {} + fdb_entries = [] keys = db.keys(db.ASIC_DB, 'ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:{*\"vlan\":\"%d\"}' % vlan_id) keys = [] if keys is None else keys for key in keys: @@ -112,28 +110,27 @@ def get_fdb(db, vlan_id, bridge_id_2_iface): mac = str(key_obj['mac']) if not is_mac_unicast(mac): continue - available_macs.add(mac.lower()) - mac = mac.replace(':', '-') - # FIXME: mac is unicast + available_macs.add((vlan_name, mac.lower())) + fdb_mac = mac.replace(':', '-') # get attributes value = db.get_all(db.ASIC_DB, key) - type = fdb_types[value['SAI_FDB_ENTRY_ATTR_TYPE']] + fdb_type = fdb_types[value['SAI_FDB_ENTRY_ATTR_TYPE']] if value['SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID'] not in bridge_id_2_iface: continue - port = bridge_id_2_iface[value['SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID']] + fdb_port = bridge_id_2_iface[value['SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID']] obj = { - 'FDB_TABLE:Vlan%d:%s' % (vlan_id, mac) : { - 'type': type, - 'port': port, + 'FDB_TABLE:Vlan%d:%s' % (vlan_id, fdb_mac) : { + 'type': fdb_type, + 'port': fdb_port, }, 'OP': 'SET' } - entries.append(obj) - - return entries, available_macs + fdb_entries.append(obj) + map_mac_ip[mac.lower()] = fdb_port + return fdb_entries, available_macs, map_mac_ip def generate_fdb_entries(filename): fdb_entries = [] @@ -146,9 +143,10 @@ def generate_fdb_entries(filename): vlan_ifaces = get_vlan_ifaces() all_available_macs = set() + map_mac_ip_per_vlan = {} for vlan in vlan_ifaces: vlan_id = int(vlan.replace('Vlan', '')) - fdb_entry, available_macs = get_fdb(db, vlan_id, bridge_id_2_iface) + fdb_entry, available_macs, map_mac_ip_per_vlan[vlan] = get_fdb(db, vlan, vlan_id, bridge_id_2_iface) all_available_macs |= available_macs fdb_entries.extend(fdb_entry) @@ -157,11 +155,69 @@ def generate_fdb_entries(filename): with open(filename, 'w') as fp: json.dump(fdb_entries, fp, indent=2, separators=(',', ': ')) - return all_available_macs + return all_available_macs, map_mac_ip_per_vlan + +def get_if(iff, cmd): + s = socket.socket() + ifreq = ioctl(s, cmd, struct.pack("16s16x",iff)) + s.close() + return ifreq + +def get_iface_mac_addr(iff): + SIOCGIFHWADDR = 0x8927 # Get hardware address + return get_if(iff, SIOCGIFHWADDR)[18:24] + +def get_iface_ip_addr(iff): + SIOCGIFADDR = 0x8915 # Get ip address + return get_if(iff, SIOCGIFADDR)[20:24] + +def send_arp(s, src_mac, src_ip, dst_mac_s, dst_ip_s): + # convert dst_mac in binary + dst_ip = socket.inet_aton(dst_ip_s) + + # convert dst_ip in binary + dst_mac = binascii.unhexlify(dst_mac_s.replace(':', '')) + + # make ARP packet + pkt = dst_mac + src_mac + ARP_CHUNK + src_mac + src_ip + dst_mac + dst_ip + ARP_PAD + + # send it + s.send(pkt) + + return + +def garp_send(arp_entries, map_mac_ip_per_vlan): + ETH_P_ALL = 0x03 + + # generate source ip addresses for arp packets + src_ip_addrs = {vlan_name:get_iface_ip_addr(vlan_name) for vlan_name,_,_ in arp_entries} + + # generate source mac addresses for arp packets + src_ifs = {map_mac_ip_per_vlan[vlan_name][dst_mac] for vlan_name, dst_mac, _ in arp_entries} + src_mac_addrs = {src_if:get_iface_mac_addr(src_if) for src_if in src_ifs} + + # open raw sockets for all required interfaces + sockets = {} + for src_if in src_ifs: + sockets[src_if] = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(ETH_P_ALL)) + sockets[src_if].bind((src_if, 0)) + + # send arp packets + for vlan_name, dst_mac, dst_ip in arp_entries: + src_if = map_mac_ip_per_vlan[vlan_name][dst_mac] + send_arp(sockets[src_if], src_mac_addrs[src_if], src_ip_addrs[vlan_name], dst_mac, dst_ip) + + # close the raw sockets + for s in sockets.values(): + s.close() + + return + def main(): - all_available_macs = generate_fdb_entries('/tmp/fdb.json') - generate_arp_entries('/tmp/arp.json', all_available_macs) + all_available_macs, map_mac_ip_per_vlan = generate_fdb_entries('/tmp/fdb.json') + arp_entries = generate_arp_entries('/tmp/arp.json', all_available_macs) + garp_send(arp_entries, map_mac_ip_per_vlan) return From 5ad84866491e7d4cf4cebcb96e469423c9c91961 Mon Sep 17 00:00:00 2001 From: Marian Pritsak Date: Tue, 26 Dec 2017 11:53:23 +0200 Subject: [PATCH 24/32] [acl-loader]: Add 'delete' command (#180) Signed-off-by: marian-pritsak --- acl_loader/main.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/acl_loader/main.py b/acl_loader/main.py index a06cb924f8..b139b3641b 100644 --- a/acl_loader/main.py +++ b/acl_loader/main.py @@ -394,6 +394,19 @@ def incremental_update(self): self.configdb.mod_entry(self.ACL_RULE, key, None) self.configdb.mod_entry(self.ACL_RULE, key, self.rules_info[key]) + + def delete(self, table=None, rule=None): + """ + :param table: + :param rule: + :return: + """ + for key in self.rules_db_info.iterkeys(): + if not table or table == key[0]: + if not rule or rule == key[1]: + self.configdb.set_entry(self.ACL_RULE, key, None) + + def show_table(self, table_name): """ Show ACL table configuration. @@ -602,6 +615,19 @@ def incremental(ctx, filename, session_name, max_priority): acl_loader.incremental_update() +@cli.command() +@click.argument('table', required=False) +@click.argument('rule', required=False) +@click.pass_context +def delete(ctx, table, rule): + """ + Delete ACL rules. + """ + acl_loader = ctx.obj["acl_loader"] + + acl_loader.delete(table, rule) + + if __name__ == "__main__": try: cli() From 6823ce2f3e46d9bbf5fcfa6b371705a7368929cb Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Tue, 9 Jan 2018 10:55:53 -0800 Subject: [PATCH 25/32] [show] Enhance/fix 'show ip/ipv6 bgp neighbors ...' commands (#183) --- show/bgp_quagga_v4.py | 18 +++++++++++++----- show/bgp_quagga_v6.py | 5 +++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/show/bgp_quagga_v4.py b/show/bgp_quagga_v4.py index be7d71508b..d543b6a1b9 100644 --- a/show/bgp_quagga_v4.py +++ b/show/bgp_quagga_v4.py @@ -25,11 +25,19 @@ def summary(): # 'neighbors' subcommand ("show ip bgp neighbors") @bgp.command() @click.argument('ipaddress', required=False) -def neighbors(ipaddress): +@click.argument('info_type', type=click.Choice(['routes', 'advertised-routes', 'received-routes']), required=False) +def neighbors(ipaddress, info_type): """Show IP (IPv4) BGP neighbors""" + command = 'sudo vtysh -c "show ip bgp neighbor' + if ipaddress is not None: - command = 'sudo vtysh -c "show ip bgp neighbor {} "'.format(ipaddress) - run_command(command) - else: - run_command('sudo vtysh -c "show ip bgp neighbor"') + command += ' {}'.format(ipaddress) + + # info_type is only valid if ipaddress is specified + if info_type is not None: + command += ' {}'.format(info_type) + + command += '"' + + run_command(command) diff --git a/show/bgp_quagga_v6.py b/show/bgp_quagga_v6.py index 27101c914f..06ef6a4df8 100644 --- a/show/bgp_quagga_v6.py +++ b/show/bgp_quagga_v6.py @@ -25,7 +25,8 @@ def summary(): # 'neighbors' subcommand ("show ipv6 bgp neighbors") @bgp.command() @click.argument('ipaddress', required=True) -def neighbors(ipaddress): +@click.argument('info_type', type=click.Choice(['routes', 'advertised-routes', 'received-routes']), required=True) +def neighbors(ipaddress, info_type): """Show IPv6 BGP neighbors""" - command = 'sudo vtysh -c "show ipv6 bgp neighbor {} "'.format(ipaddress) + command = 'sudo vtysh -c "show ipv6 bgp neighbor {} {}"'.format(ipaddress, info_type) run_command(command) From 94e2bb073cf46eb99cc6142c7806f90f2bf1e0a6 Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Thu, 11 Jan 2018 09:07:06 -0800 Subject: [PATCH 26/32] [show] Add and rename 'show interfaces transceiver' subcommands (eeprom, lpmode, presence) (#182) --- show/main.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/show/main.py b/show/main.py index ff302627a9..a0fdfd3cf8 100755 --- a/show/main.py +++ b/show/main.py @@ -231,11 +231,15 @@ def transceiver(): @transceiver.command() @click.argument('interfacename', required=False) -def basic(interfacename): - """Show basic interface transceiver information""" +@click.option('-d', '--dom', 'dump_dom', is_flag=True, help="Also display Digital Optical Monitoring (DOM) data") +def eeprom(interfacename, dump_dom): + """Show interface transceiver EEPROM information""" command = "sudo sfputil show eeprom" + if dump_dom: + command += " --dom" + if interfacename is not None: command += " -p {}".format(interfacename) @@ -244,10 +248,22 @@ def basic(interfacename): @transceiver.command() @click.argument('interfacename', required=False) -def details(interfacename): - """Show interface transceiver details (Digital Optical Monitoring)""" +def lpmode(interfacename): + """Show interface transceiver low-power mode status""" + + command = "sudo sfputil show lpmode" + + if interfacename is not None: + command += " -p {}".format(interfacename) + + run_command(command) + +@transceiver.command() +@click.argument('interfacename', required=False) +def presence(interfacename): + """Show interface transceiver presence""" - command = "sudo sfputil show eeprom --dom" + command = "sudo sfputil show presence" if interfacename is not None: command += " -p {}".format(interfacename) From 1ce88d9c13d91ff590d24693150cab05cef05122 Mon Sep 17 00:00:00 2001 From: sihuihan88 Date: Thu, 11 Jan 2018 19:39:57 -0800 Subject: [PATCH 27/32] [pfcwd]:Set correct time range for parameters (#185) Signed-off-by: Sihui Han --- pfcwd/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pfcwd/main.py b/pfcwd/main.py index eb95e17fce..22f636d25f 100644 --- a/pfcwd/main.py +++ b/pfcwd/main.py @@ -98,9 +98,9 @@ def config(ports): # Start WD @cli.command() @click.option('--action', '-a', type=click.Choice(['drop', 'forward', 'alert'])) -@click.option('--restoration-time', '-r', type=click.IntRange(100, 5000)) +@click.option('--restoration-time', '-r', type=click.IntRange(100, 60000)) @click.argument('ports', nargs = -1) -@click.argument('detection-time', type=click.IntRange(100, 60000)) +@click.argument('detection-time', type=click.IntRange(100, 5000)) def start(action, restoration_time, ports, detection_time): """ Start PFC watchdog on port(s) """ configdb = swsssdk.ConfigDBConnector() From 495584bd2f228af81934b23e4cf19648ab387fab Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Fri, 12 Jan 2018 16:59:20 -0800 Subject: [PATCH 28/32] Remove platform-specific hardware plugin packages; They have been moved to sonic-platform-common repo (#184) - Also increment package version to 1.2 --- README.md | 4 +- setup.py | 18 +- sonic_eeprom/__init__.py | 0 sonic_eeprom/eeprom_base.py | 359 ----------- sonic_eeprom/eeprom_dts.py | 141 ----- sonic_eeprom/eeprom_tlvinfo.py | 545 ----------------- sonic_psu/__init__.py | 0 sonic_psu/psu_base.py | 49 -- sonic_sfp/__init__.py | 0 sonic_sfp/bcmshell.py | 496 --------------- sonic_sfp/sff8436.py | 908 --------------------------- sonic_sfp/sff8472.py | 1050 -------------------------------- sonic_sfp/sffbase.py | 250 -------- sonic_sfp/sfputilbase.py | 608 ------------------ 14 files changed, 6 insertions(+), 4422 deletions(-) delete mode 100644 sonic_eeprom/__init__.py delete mode 100644 sonic_eeprom/eeprom_base.py delete mode 100644 sonic_eeprom/eeprom_dts.py delete mode 100644 sonic_eeprom/eeprom_tlvinfo.py delete mode 100644 sonic_psu/__init__.py delete mode 100644 sonic_psu/psu_base.py delete mode 100644 sonic_sfp/__init__.py delete mode 100644 sonic_sfp/bcmshell.py delete mode 100644 sonic_sfp/sff8436.py delete mode 100644 sonic_sfp/sff8472.py delete mode 100644 sonic_sfp/sffbase.py delete mode 100644 sonic_sfp/sfputilbase.py diff --git a/README.md b/README.md index 7c7a56679a..b419224f55 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Command-line utilities for SONiC # Contribution guide -All contributors must sign a contribution license agreement before contributions can be accepted. Contact kasubra@microsoft.com or daloher@microsoft.com. Later this will be automated. +All contributors must sign a contribution license agreement (CLA) before contributions can be accepted. This process is now automated via a GitHub bot when submitting new pull request. If the contributor has not yet signed a CLA, the bot will create a comment on the pull request containing a link to electronically sign the CLA. ### GitHub Workflow @@ -34,7 +34,7 @@ For example: > * Health-Monitor - The idea of the patch is that if something went wrong with the notification channel, > we will have the option to know about it (Query the LLEN table length). > -> Signed-off-by: user@dev.null +> Signed-off-by: John Doe user@dev.null * Each developer should fork this repository and [add the team as a Contributor](https://help.github.com/articles/adding-collaborators-to-a-personal-repository) diff --git a/setup.py b/setup.py index 6e5e524413..bc23ad9d29 100644 --- a/setup.py +++ b/setup.py @@ -1,21 +1,15 @@ import glob -import sys from setuptools import setup import unittest -# For python3 wheel, we currently don't need test -# TODO: build python3 wheel of all the dependencies, and remove conditional test def get_test_suite(): - if sys.version_info >= (3, 0): - return unittest.TestSuite() - else: - test_loader = unittest.TestLoader() - test_suite = test_loader.discover('sonic-utilities-tests', pattern='*.py') - return test_suite + test_loader = unittest.TestLoader() + test_suite = test_loader.discover('sonic-utilities-tests', pattern='*.py') + return test_suite setup( name='sonic-utilities', - version='1.1', + version='1.2', description='Command-line utilities for SONiC', license='Apache 2.0', author='SONiC Team', @@ -32,10 +26,7 @@ def get_test_suite(): 'sfputil', 'psuutil', 'show', - 'sonic_eeprom', 'sonic_installer', - 'sonic_psu', - 'sonic_sfp', 'sonic-utilities-tests', 'undebug', ], @@ -92,7 +83,6 @@ def get_test_suite(): 'Natural Language :: English', 'Operating System :: POSIX :: Linux', 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.6', 'Topic :: Utilities', ], keywords='sonic SONiC utilities command line cli CLI', diff --git a/sonic_eeprom/__init__.py b/sonic_eeprom/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sonic_eeprom/eeprom_base.py b/sonic_eeprom/eeprom_base.py deleted file mode 100644 index c41ea5967d..0000000000 --- a/sonic_eeprom/eeprom_base.py +++ /dev/null @@ -1,359 +0,0 @@ -#! /usr/bin/python -# Copyright 2012 Cumulus Networks LLC, all rights reserved - -############################################################################# -# Base eeprom class containing the main logic for reading, writing, and -# setting the eeprom. The format definition is a list of tuples of: -# ('data name', 'data type', 'size in bytes') -# data type is one of 's', 'C', and 'x' (string, char, and ignore) -# 'burn' as a data name indicates the corresponding number of bytes are to -# be ignored - -try: - import exceptions - import binascii - import optparse - import os - import io - import sys - import struct - import subprocess - import fcntl -except ImportError, e: - raise ImportError (str(e) + "- required module not found") - - -class EepromDecoder(object): - def __init__(self, path, format, start, status, readonly): - self.p = path - self.f = format - self.s = start - self.u = status - self.r = readonly - self.cache_name = None - self.cache_update_needed = False - self.lock_file = None - - def check_status(self): - if self.u <> '': - F = open(self.u, "r") - d = F.readline().rstrip() - F.close() - return d - else: - return 'ok' - - def set_cache_name(self, name): - # before accessing the eeprom we acquire an exclusive lock on the eeprom file. - # this will prevent a race condition where multiple instances of this app - # could try to update the cache at the same time - self.cache_name = name - self.lock_file = open(self.p, 'r') - fcntl.flock(self.lock_file, fcntl.LOCK_EX) - - def is_read_only(self): - return self.r - - def decoder(self, s, t): - return t - - def encoder(self, I, v): - return v - - def checksum_field_size(self): - return 4 # default - - def is_checksum_field(self, I): - return I[0] == 'crc' # default - - def checksum_type(self): - return 'crc32' - - def encode_checksum(self, crc): - if self.checksum_field_size() == 4: - return struct.pack('>I', crc) - elif self.checksum_field_size() == 1: - return struct.pack('>B', crc) - print 'checksum type not yet supported' - exit(1) - - def compute_2s_complement(self, e, size): - crc = 0 - loc = 0 - end = len(e) - while loc <> end: - crc += int('0x' + binascii.b2a_hex(e[loc:loc+size]), 0) - loc += size - T = 1 << (size * 8) - return (T - crc) & (T - 1) - - def compute_dell_crc(self, message): - poly = 0x8005 - reg = 0x0000 - message += '\x00\x00' - for byte in message: - mask = 0x80 - while (mask > 0): - reg<<=1 - if ord(byte) & mask: - reg += 1 - mask>>=1 - if reg > 0xffff: - reg &= 0xffff - reg ^= poly - return reg - - def calculate_checksum(self, e): - if self.checksum_type() == 'crc32': - return binascii.crc32(e) & 0xffffffff - - if self.checksum_type() == '2s-complement': - size = self.checksum_field_size() - return self.compute_2s_complement(e, size) - - if self.checksum_type() == 'dell-crc': - return self.compute_dell_crc(e) - print 'checksum type not yet supported' - exit(1) - - def is_checksum_valid(self, e): - offset = 0 - self.checksum_field_size() - crc = self.calculate_checksum(e[:offset]) - - loc = 0 - for I in self.f: - end = loc + I[2] - t = e[loc:end] - loc = end - if self.is_checksum_field(I): - i = self.decoder(I[0], t) - if int(i, 0) == crc: - return (True, crc) - else: - return (False, crc) - else: - continue - return (False, crc) - - def decode_eeprom(self, e): - loc = 0 - for I in self.f: - end = loc + I[2] - t = e[loc:end] - loc = end - if I[0] == 'burn': - continue - elif I[1] == 's': - i = t - else: - i = self.decoder(I[0], t) - print "%-20s: %s" %(I[0], i) - - def set_eeprom(self, e, cmd_args): - line = '' - loc = 0 - ndict = {} - fields = list(I[0] for I in list(self.f)) - if len(cmd_args): - for arg in cmd_args[0].split(','): - k, v = arg.split('=') - k = k.strip() - v = v.strip() - if k not in fields: - print "Error: invalid field '%s'" %(k) - exit(1) - ndict[k] = v - - for I in self.f: - # print the original value - end = loc + I[2] - sl = e[loc:end] - loc = end - if I[0] == 'burn': - #line += sl - # fill with zeros - line = line.ljust(len(line) + I[2], '\x00') - continue - elif I[1] == 's': - i = sl - else: - i = self.decoder(I[0], sl) - - if len(cmd_args) == 0: - if self.is_checksum_field(I): - print ("%-20s: %s " %(I[0], i)) - continue - - # prompt for new value - v = raw_input("%-20s: [%s] " %(I[0], i)) - if v == '': - v = i - else: - if I[0] not in ndict.keys(): - v = i - else: - v = ndict[I[0]] - - line += self.encoder(I, v) - - # compute and append crc at the end - crc = self.encode_checksum(self.calculate_checksum(line)) - - line += crc - - return line - - def open_eeprom(self): - ''' - Open the EEPROM device file. - If a cache file exists, use that instead of the EEPROM. - ''' - using_eeprom = True - eeprom_file = self.p - try: - if os.path.isfile(self.cache_name): - eeprom_file = self.cache_name - using_eeprom = False - except: - pass - self.cache_update_needed = using_eeprom - return io.open(eeprom_file, "rb") - - def read_eeprom(self): - sizeof_info = 0 - for I in self.f: - sizeof_info += I[2] - o = self.read_eeprom_bytes(sizeof_info) - return o - - def read_eeprom_bytes(self, byteCount, offset=0): - F = self.open_eeprom() - F.seek(self.s + offset) - o = F.read(byteCount) - if len(o) != byteCount: - raise RuntimeError("expected to read %d bytes from %s, " \ - %(byteCount, self.p) + - "but only read %d" %(len(o))) - F.close() - return o - - def write_eeprom(self, e): - F = open(self.p, "wb") - F.seek(self.s) - F.write(e) - F.close() - self.write_cache(e) - - def write_cache(self, e): - if self.cache_name: - F = open(self.cache_name, "wb") - F.seek(self.s) - F.write(e) - F.close() - - def update_cache(self, e): - if self.cache_update_needed: - self.write_cache(e) - fcntl.flock(self.lock_file, fcntl.LOCK_UN) - - def diff_mac(self, mac1, mac2): - if mac1 == '' or mac2 == '': - return 0 - mac1_octets = [] - mac1_octets = mac1.split(':') - mac1val = int(mac1_octets[5], 16) | int(mac1_octets[4], 16) << 8 | int(mac1_octets[3], 16) << 16 - mac2_octets = [] - mac2_octets = mac2.split(':') - mac2val = int(mac2_octets[5], 16) | int(mac2_octets[4], 16) << 8 | int(mac2_octets[3], 16) << 16 - # check oui matches - if (mac1_octets[0] != mac2_octets[0] - or mac1_octets[1] != mac2_octets[1] - or mac1_octets[2] != mac2_octets[2]) : - return 0 - - if mac2val < mac1val: - return 0 - - return (mac2val - mac1val) - - def increment_mac(self, mac): - if mac != "": - mac_octets = [] - mac_octets = mac.split(':') - ret_mac = int(mac_octets[5], 16) | int(mac_octets[4], 16) << 8 | int(mac_octets[3], 16) << 16 - ret_mac = ret_mac + 1 - - if (ret_mac & 0xff000000): - print 'Error: increment carries into OUI' - return '' - - mac_octets[5] = hex(ret_mac & 0xff)[2:].zfill(2) - mac_octets[4] = hex((ret_mac >> 8) & 0xff)[2:].zfill(2) - mac_octets[3] = hex((ret_mac >> 16) & 0xff)[2:].zfill(2) - - return ':'.join(mac_octets).upper() - - return '' - - @classmethod - def find_field(cls, e, name): - if not hasattr(cls, 'brd_fmt'): - raise RuntimeError("Class %s does not have brb_fmt" % cls) - if not e: - raise RuntimeError("EEPROM can not be empty") - brd_fmt = cls.brd_fmt - loc = 0 - for f in brd_fmt: - end = loc + f[2] - t = e[loc:end] - loc = end - if f[0] == name: - return t - - def base_mac_addr(self, e): - ''' - Returns the base MAC address found in the EEPROM. - - Sub-classes must override this method as reading the EEPROM - and finding the base MAC address entails platform specific - details. - - See also mgmtaddrstr() and switchaddrstr(). - ''' - print "ERROR: Platform did not implement base_mac_addr()" - raise NotImplementedError - - def mgmtaddrstr(self, e): - ''' - Returns the base MAC address to use for the Ethernet - management interface(s) on the CPU complex. - - By default this is the same as the base MAC address listed in - the EEPROM. - - See also switchaddrstr(). - ''' - return self.base_mac_addr(e) - - def switchaddrstr(self, e): - ''' - Returns the base MAC address to use for the switch ASIC - interfaces. - - By default this is *next* address after the base MAC address - listed in the EEPROM. - - See also mgmtaddrstr(). - ''' - return self.increment_mac(self.base_mac_addr(e)) - - def switchaddrrange(self, e): - # this function is in the base class only to catch errors - # the platform specific import should have an override of this method - # to provide the allocated mac range from syseeprom or flash sector or - # wherever that platform stores this info - print "Platform did not indicate allocated mac address range" - raise NotImplementedError - - def serial_number_str(self, e): - raise NotImplementedError("Platform did not indicate serial number") diff --git a/sonic_eeprom/eeprom_dts.py b/sonic_eeprom/eeprom_dts.py deleted file mode 100644 index e449f952d3..0000000000 --- a/sonic_eeprom/eeprom_dts.py +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/python -# Copyright 2012 Cumulus Networks LLC, all rights reserved - -try: - import os - import exceptions - import binascii - import subprocess -except ImportError, e: - raise ImportError (str(e) + "- required module not found") - -i2c_root = '/sys/class/i2c-adapter/' -mtd_root = '/dev/mtd' -dts_root = '/proc/device-tree/' -sys_dev = '/sys/devices/' - -# -# This routine takes a token list containing the desired eeprom types -# (e.g. 'sfp', 'psu', 'board'), and returns a dict of {[dts path:(dev attrs)]} -# for the corresponding eeproms. For those accessible via i2c, the attrs -# will contain (i2c bus index, i2c device number). For those mounted to -# /dev/mtd, the attrs will be (mtd partition number). -# -def get_dev_attr_from_dtb(tokens): - - dts_paths = [] - i2c_devices = [] - sub_devices = [] - - eep_dict = {} - - # - # first try i2c - # - try: - ph = subprocess.Popen(['/bin/ls', '-R', dts_root], - stdout=subprocess.PIPE, - shell=False, stderr=subprocess.STDOUT) - cmdout = ph.communicate()[0] - ph.wait() - except OSError, e: - raise OSError("cannot access directory") - - lines = cmdout.splitlines() - for I in lines: - if not I.endswith(':') or 'i2c' not in I: - continue - - I = I.rstrip(':\n\r') - last = I.split('/')[-1] - if 'i2c' in last: - depth = I.count('i2c') - while len(sub_devices) < depth: - sub_devices.append([]) - sub_devices[depth-1].append(I) - elif 'eeprom' in last: - dts_paths.append(I) - - # re-order the device heirarchy and build the device list - for i in sub_devices: - for j in i: - i2c_devices.append(j) - - for eep in dts_paths: - instance = '' - if os.path.isfile('/'.join([eep, 'label'])): - F = open('/'.join([eep, 'label']), "rb") - instance = F.read().partition('_eeprom')[0] - F.close() - - # check for read-only - read_only = os.path.isfile('/'.join([eep, 'read-only'])) - - for t in tokens: - if t not in eep and t not in instance: - continue - - # get the i2c idx by matching the path prefix - i2c_idx = i2c_devices.index(eep.rsplit('/', 1)[0]) - - # read the reg - reg_path = '/'.join([eep, 'reg']) - F = open(reg_path, "rb") - o = F.read() - reg = binascii.b2a_hex(o)[-2:] - F.close() - - eep_dict[eep] = {'type': 'i2c', \ - 'dev-id': i2c_idx, \ - 'reg': reg, \ - 'ro': read_only} - break - - - # - # now try flash - # - try: - ph = subprocess.Popen(['/bin/grep', '-r', 'eeprom', dts_root], - stdout=subprocess.PIPE, - shell=False, stderr=subprocess.STDOUT) - cmdout = ph.communicate()[0] - ph.wait() - except OSError, e: - raise OSError("cannot access directory") - - lines = cmdout.splitlines() - for I in lines: - if 'flash' not in I or 'label' not in I: - continue - - eep = I.partition(dts_root)[2].rpartition('label')[0] - full_eep = '/'.join([dts_root, eep]) - F = open('/'.join([full_eep, 'label']), "rb") - instance = F.read().partition('_eeprom')[0] - F.close() - - read_only = os.path.isfile('/'.join([full_eep, 'read-only'])) - - for t in tokens: - if t not in instance: - continue - - mtd_n = eep.partition('partition@')[2].rstrip('/') - eep_dict[full_eep] = {'type': 'mtd', \ - 'dev-id': mtd_n, \ - 'ro': read_only} - - return eep_dict - - -def dev_attr_to_path(dev_attrs): - dev_path = '' - if dev_attrs['type'] == 'i2c': - dev_path = i2c_root + 'i2c-' + str(dev_attrs['dev-id']) + '/' + \ - str(dev_attrs['dev-id']) + '-00' + str(dev_attrs['reg']) + \ - '/' + 'eeprom' - elif dev_attrs['type'] == 'mtd': - dev_path = mtd_root + dev_attrs['dev-id'] - - return dev_path diff --git a/sonic_eeprom/eeprom_tlvinfo.py b/sonic_eeprom/eeprom_tlvinfo.py deleted file mode 100644 index ca4b073be0..0000000000 --- a/sonic_eeprom/eeprom_tlvinfo.py +++ /dev/null @@ -1,545 +0,0 @@ -#! /usr/bin/python -# Copyright 2012 Cumulus Networks LLC, all rights reserved - -############################################################################# -# Base eeprom class containing the main logic for reading, writing, and -# setting the eeprom. The format definition is a list of tuples of: -# ('data name', 'data type', 'size in bytes') -# data type is one of 's', 'C', and 'x' (string, char, and ignore) -# 'burn' as a data name indicates the corresponding number of bytes are to -# be ignored - -try: - import exceptions - import binascii - import optparse - import os - import sys - import eeprom_base -except ImportError, e: - raise ImportError (str(e) + "- required module not found") - - -# -# TlvInfo Format - This eeprom format was defined by Cumulus Networks -# and can be found here: -# -# -class TlvInfoDecoder(eeprom_base.EepromDecoder): - - # Header Field Constants - _TLV_INFO_ID_STRING = "TlvInfo\x00" - _TLV_INFO_VERSION = 0x01 - _TLV_INFO_HDR_LEN = 11 - _TLV_INFO_MAX_LEN = 2048 - _TLV_TOTAL_LEN_MAX = _TLV_INFO_MAX_LEN - _TLV_INFO_HDR_LEN - _TLV_HDR_ENABLED = 1 - - # The Undefined TLV Type - _TLV_CODE_UNDEFINED = 0xFC - - # Default TLV Types - _TLV_CODE_PRODUCT_NAME = 0x21 - _TLV_CODE_PART_NUMBER = 0x22 - _TLV_CODE_SERIAL_NUMBER = 0x23 - _TLV_CODE_MAC_BASE = 0x24 - _TLV_CODE_MANUF_DATE = 0x25 - _TLV_CODE_DEVICE_VERSION = 0x26 - _TLV_CODE_LABEL_REVISION = 0x27 - _TLV_CODE_PLATFORM_NAME = 0x28 - _TLV_CODE_ONIE_VERSION = 0x29 - _TLV_CODE_MAC_SIZE = 0x2A - _TLV_CODE_MANUF_NAME = 0x2B - _TLV_CODE_MANUF_COUNTRY = 0x2C - _TLV_CODE_VENDOR_NAME = 0x2D - _TLV_CODE_DIAG_VERSION = 0x2E - _TLV_CODE_SERVICE_TAG = 0x2F - _TLV_CODE_VENDOR_EXT = 0xFD - _TLV_CODE_CRC_32 = 0xFE - - # By default disable the Quanta specific codes - _TLV_CODE_QUANTA_MAGIC = _TLV_CODE_UNDEFINED - _TLV_CODE_QUANTA_CRC = _TLV_CODE_UNDEFINED - _TLV_CODE_QUANTA_CARD_TYPE = _TLV_CODE_UNDEFINED - _TLV_CODE_QUANTA_HW_VERSION = _TLV_CODE_UNDEFINED - _TLV_CODE_QUANTA_SW_VERSION = _TLV_CODE_UNDEFINED - _TLV_CODE_QUANTA_MANUF_DATE = _TLV_CODE_UNDEFINED - _TLV_CODE_QUANTA_MODEL_NAME = _TLV_CODE_UNDEFINED - - # TLV Value Display Switch - _TLV_DISPLAY_VENDOR_EXT = False - - def __init__(self, path, start, status, ro, max_len=_TLV_INFO_MAX_LEN): - super(TlvInfoDecoder, self).__init__(path, \ - None, \ - start, \ - status, \ - ro) - self.eeprom_start = start - self.eeprom_max_len = max_len - - def decode_eeprom(self, e): - ''' - Decode and print out the contents of the EEPROM. - ''' - if self._TLV_HDR_ENABLED : - if not self.is_valid_tlvinfo_header(e): - print "EEPROM does not contain data in a valid TlvInfo format." - return - - print "TlvInfo Header:" - print " Id String: %s" % (e[0:7],) - print " Version: %d" % (ord(e[8]),) - total_len = (ord(e[9]) << 8) | ord(e[10]) - print " Total Length: %d" % (total_len,) - tlv_index = self._TLV_INFO_HDR_LEN - tlv_end = self._TLV_INFO_HDR_LEN + total_len - else : - tlv_index = self.eeprom_start - tlv_end = self._TLV_INFO_MAX_LEN - - print "TLV Name Code Len Value" - print "-------------------- ---- --- -----" - while (tlv_index + 2) < len(e) and tlv_index < tlv_end: - if not self.is_valid_tlv(e[tlv_index:]): - print "Invalid TLV field starting at EEPROM offset %d" % (tlv_index,) - return - print self.decoder(None, e[tlv_index:tlv_index + 2 + ord(e[tlv_index + 1])]) - if ord(e[tlv_index]) == self._TLV_CODE_QUANTA_CRC or \ - ord(e[tlv_index]) == self._TLV_CODE_CRC_32: - return - tlv_index += ord(e[tlv_index+1]) + 2 - - def set_eeprom(self, e, cmd_args): - ''' - Returns the new contents of the EEPROM. If command line arguments are supplied, - then those fields are overwritten or added to the existing EEPROM contents. If - not command line arguments are supplied, the user is prompted for the contents - of the EEPROM. - ''' - new_tlvs = "" - (crc_is_valid, crc) = self.is_checksum_valid(e) - if crc_is_valid: - if self._TLV_HDR_ENABLED: - tlv_index = self._TLV_INFO_HDR_LEN - tlv_end = self._TLV_INFO_HDR_LEN + ((ord(e[9]) << 8) | ord(e[10])) - else : - tlv_index = self.eeprom_start - tlv_end = self._TLV_INFO_MAX_LEN - - while tlv_index < len(e) and \ - tlv_index < tlv_end and \ - self.is_valid_tlv(e[tlv_index:]) and \ - ord(e[tlv_index]) != self._TLV_CODE_CRC_32 and \ - ord(e[tlv_index]) != self._TLV_CODE_QUANTA_CRC: - new_tlvs += e[tlv_index:tlv_index + 2 + ord(e[tlv_index + 1])] - tlv_index += ord(e[tlv_index+1]) + 2 - - if len(cmd_args): - for arg_str in cmd_args: - for arg in arg_str.split(','): - k, v = arg.split('=') - k = int(k.strip(), 0) - v = v.strip() - new_tlv = self.encoder((k,), v) - (tlv_found, index) = self.get_tlv_index(new_tlvs, k) - if tlv_found: - new_tlvs = new_tlvs[:index] + new_tlv + \ - new_tlvs[index + 2 + ord(new_tlvs[index + 1]):] - else: - new_tlvs += new_tlv - - else: - action = "a" - while action not in ['f', 'F']: - - action = raw_input("\n[a]dd, [m]odify, [d]elete, [f]inished: ") - if action in ['a', 'A', 'm', 'M']: - code = raw_input("Enter a TLV type code: ") - code = int(code, 0) - value = raw_input("Enter the value: ") - new_tlv = self.encoder((code,), value) - (tlv_found, index) = self.get_tlv_index(new_tlvs, code) - if tlv_found: - new_tlvs = new_tlvs[:index] + new_tlv + \ - new_tlvs[index + 2 + ord(new_tlvs[index + 1]):] - else: - new_tlvs += new_tlv - elif action in ['d', 'D']: - code = raw_input("Enter a TLV type code: ") - code = int(code, 0) - (tlv_found, index) = self.get_tlv_index(new_tlvs, code) - if tlv_found: - new_tlvs = new_tlvs[:index] + \ - new_tlvs[index + 2 + ord(new_tlvs[index + 1]):] - elif action in ['f', 'F']: - pass - else: - print "\nInvalid input, please enter 'a', 'm', 'd', or 'f'\n" - - if self._TLV_HDR_ENABLED: - new_tlvs_len = len(new_tlvs) + 6 - new_e = self._TLV_INFO_ID_STRING + chr(self._TLV_INFO_VERSION) + \ - chr((new_tlvs_len >> 8) & 0xFF) + \ - chr(new_tlvs_len & 0xFF) + new_tlvs - else: - new_e = new_tlvs - - if self._TLV_CODE_CRC_32 != self._TLV_CODE_UNDEFINED: - new_e = new_e + chr(self._TLV_CODE_CRC_32) + chr(4) - elif self._TLV_CODE_QUANTA_CRC != self._TLV_CODE_UNDEFINED: - new_e = new_e + chr(self._TLV_CODE_QUANTA_CRC) + chr(2) - else: - print "\nFailed to formulate new eeprom\n" - exit - new_e += self.encode_checksum(self.calculate_checksum(new_e)) - self.decode_eeprom(new_e) - if len(new_e) > min(self._TLV_INFO_MAX_LEN, self.eeprom_max_len): - sys.stderr.write("\nERROR: There is not enough room in the EEPROM to save data.\n") - exit(1) - return new_e - - def is_valid_tlvinfo_header(self, e): - ''' - Perform sanity checks on the first 11 bytes of the TlvInfo EEPROM - data passed in as a string. - 1. Large enough to hold the header - 2. First 8 bytes contain null-terminated ASCII string "TlvInfo" - 3. Version byte is 1 - 4. Total length bytes contain value which is less than or equal - to the allowed maximum (2048-11) - ''' - return len(e) >= self._TLV_INFO_HDR_LEN and \ - e[0:8] == self._TLV_INFO_ID_STRING and \ - ord(e[8]) == self._TLV_INFO_VERSION and \ - ((ord(e[9]) << 8) | ord(e[10])) <= self._TLV_TOTAL_LEN_MAX - - def is_valid_tlv(self, e): - ''' - Perform basic sanity checks on a TLV field. The TLV is in the string - provided. - 1. The TLV is at least 2 bytes long - 2. The length byte contains a value which does not cause the value - field to go beyond the length of the string. - ''' - return (len(e) >= 2 and (2 + ord(e[1]) <= len(e))) - - def is_checksum_valid(self, e): - ''' - Validate the checksum in the provided TlvInfo EEPROM data. - ''' - if not self.is_valid_tlvinfo_header(e): - return (False, 0) - - offset = self._TLV_INFO_HDR_LEN + ((ord(e[9]) << 8) | ord(e[10])) - if len(e) < offset or \ - ord(e[offset-6]) != self._TLV_CODE_CRC_32 or \ - ord(e[offset-5]) != 4: - return (False, 0) - - crc = self.calculate_checksum(e[:offset-4]) - tlv_crc = ord(e[offset-4]) << 24 | ord(e[offset-3]) << 16 | \ - ord(e[offset-2]) << 8 | ord(e[offset-1]) - if tlv_crc == crc: - return(True, crc) - - return (False, crc) - - def read_eeprom(self): - ''' - Read the eeprom contents. This is performed in two steps. First - the 11 bytes of the TlvInfo structure (the header) are read and - sanity checked. Then using the total length field in the header, - the rest of the data is read from the EEPROM. - ''' - offset = 0 - if self._TLV_HDR_ENABLED: - h = self.read_eeprom_bytes(self._TLV_INFO_HDR_LEN) - offset = self._TLV_INFO_HDR_LEN - if len(h) != self._TLV_INFO_HDR_LEN: - raise RuntimeError("expected to read %d bytes from %s, " \ - %(self._TLV_INFO_HDR_LEN, self.p) + \ - "but only read %d" %(len(h),)) - if not self.is_valid_tlvinfo_header(h): - return h - sizeof_tlvs = (ord(h[9]) << 8) | ord(h[10]) - else: - h = "" - sizeof_tlvs = self._TLV_INFO_MAX_LEN - - t = self.read_eeprom_bytes(sizeof_tlvs, offset) - if len(t) != sizeof_tlvs: - raise RuntimeError("expected to read %d bytes from %s, " \ - %(sizeof_tlvs, self.p) + \ - "but only read %d" %(len(t))) - return h + t - - def get_tlv_field(self, e, code): - ''' - Given an EEPROM string the TLV field for the provided code is - returned. This routine validates the EEPROM data (checksum and - other sanity checks) and then searches for a TLV field with the - supplied type code. A tuple of two items is returned. The first - item is a boolean indicating success and, if True, the second - item is a 3 element list with the type (int), length (int), - and value (string) of the requested TLV. - ''' - (is_valid, valid_crc) = self.is_checksum_valid(e) - if not is_valid: - return (False, None) - if self._TLV_HDR_ENABLED: - tlv_index = self._TLV_INFO_HDR_LEN - tlv_end = ((ord(e[9]) << 8) | ord(e[10])) + self._TLV_INFO_HDR_LEN - else : - tlv_index = self.eeprom_start - tlv_end = self._TLV_INFO_MAX_LEN - while tlv_index < len(e) and tlv_index < tlv_end: - if not self.is_valid_tlv(e[tlv_index:]): - return (False, None) - if ord(e[tlv_index]) == code: - return (True, [ord(e[tlv_index]), ord(e[tlv_index+1]), \ - e[tlv_index+2:tlv_index+2+ord(e[tlv_index+1])]]) - tlv_index += ord(e[tlv_index+1]) + 2 - return (False, None) - - def get_tlv_index(self, e, code): - ''' - Given an EEPROM string with just TLV fields (no TlvInfo header) - finds the index of the requested type code. This routine searches - for a TLV field with the supplied type code. A tuple of two items - is returned. The first item is a boolean indicating success and, - if True, the second item is the index in the supplied EEPROM string - of the matching type code. - ''' - tlv_index = 0 - while tlv_index < len(e): - if not self.is_valid_tlv(e[tlv_index:]): - return (False, 0) - if ord(e[tlv_index]) == code: - return (True, tlv_index ) - tlv_index += ord(e[tlv_index+1]) + 2 - return (False, 0) - - def base_mac_addr(self, e): - ''' - Returns the value field of the MAC #1 Base TLV formatted as a string - of colon-separated hex digits. - ''' - (is_valid, t) = self.get_tlv_field(e, self._TLV_CODE_MAC_BASE) - if not is_valid or t[1] != 6: - return super(TlvInfoDecoder, self).switchaddrstr(e) - - return ":".join([binascii.b2a_hex(T) for T in t[2]]) - - def switchaddrrange(self, e): - ''' - Returns the value field of the MAC #1 Size TLV formatted as a decimal - string - ''' - (is_valid, t) = self.get_tlv_field(e, self._TLV_CODE_MAC_SIZE) - if not is_valid: - return super(TlvInfoDecoder, self).switchaddrrange(e) - - return str((ord(t[2][0]) << 8) | ord(t[2][1])) - - def modelstr(self, e): - ''' - Returns the value field of the Product Name TLV as a string - ''' - (is_valid, t) = self.get_tlv_field(e, self._TLV_CODE_PRODUCT_NAME) - if not is_valid: - return super(TlvInfoDecoder, self).modelstr(e) - - return t[2] - - def serial_number_str(self, e): - ''' - Returns the value field of the Serial Number TLV as a string - ''' - valid, t = self.get_tlv_field(e, self._TLV_CODE_SERIAL_NUMBER) - if not valid: - return super(TlvInfoDecoder, self).serial_number_str(e) - return t[2] - - def decoder(self, s, t): - ''' - Return a string representing the contents of the TLV field. The format of - the string is: - 1. The name of the field left justified in 20 characters - 2. The type code in hex right justified in 5 characters - 3. The length in decimal right justified in 4 characters - 4. The value, left justified in however many characters it takes - The vailidity of EEPROM contents and the TLV field has been verified - prior to calling this function. The 's' parameter is unused - ''' - if ord(t[0]) == self._TLV_CODE_PRODUCT_NAME: - name = "Product Name" - value = str(t[2:2 + ord(t[1])]) - elif ord(t[0]) == self._TLV_CODE_PART_NUMBER: - name = "Part Number" - value = t[2:2 + ord(t[1])] - elif ord(t[0]) == self._TLV_CODE_SERIAL_NUMBER: - name = "Serial Number" - value = t[2:2 + ord(t[1])] - elif ord(t[0]) == self._TLV_CODE_MAC_BASE: - name = "Base MAC Address" - value = ":".join([binascii.b2a_hex(T) for T in t[2:8]]).upper() - elif ord(t[0]) == self._TLV_CODE_MANUF_DATE: - name = "Manufacture Date" - value = t[2:2 + ord(t[1])] - elif ord(t[0]) == self._TLV_CODE_DEVICE_VERSION: - name = "Device Version" - value = str(ord(t[2])) - elif ord(t[0]) == self._TLV_CODE_LABEL_REVISION: - name = "Label Revision" - value = t[2:2 + ord(t[1])] - elif ord(t[0]) == self._TLV_CODE_PLATFORM_NAME: - name = "Platform Name" - value = t[2:2 + ord(t[1])] - elif ord(t[0]) == self._TLV_CODE_ONIE_VERSION: - name = "ONIE Version" - value = t[2:2 + ord(t[1])] - elif ord(t[0]) == self._TLV_CODE_MAC_SIZE: - name = "MAC Addresses" - value = str((ord(t[2]) << 8) | ord(t[3])) - elif ord(t[0]) == self._TLV_CODE_MANUF_NAME: - name = "Manufacturer" - value = t[2:2 + ord(t[1])] - elif ord(t[0]) == self._TLV_CODE_MANUF_COUNTRY: - name = "Manufacture Country" - value = t[2:2 + ord(t[1])] - elif ord(t[0]) == self._TLV_CODE_VENDOR_NAME: - name = "Vendor Name" - value = t[2:2 + ord(t[1])] - elif ord(t[0]) == self._TLV_CODE_DIAG_VERSION: - name = "Diag Version" - value = t[2:2 + ord(t[1])] - elif ord(t[0]) == self._TLV_CODE_SERVICE_TAG: - name = "Service Tag" - value = t[2:2 + ord(t[1])] - elif ord(t[0]) == self._TLV_CODE_VENDOR_EXT: - name = "Vendor Extension" - value = "" - if self._TLV_DISPLAY_VENDOR_EXT: - for c in t[2:2 + ord(t[1])]: - value += "0x%02X " % (ord(c),) - elif ord(t[0]) == self._TLV_CODE_CRC_32 and len(t) == 6: - name = "CRC-32" - value = "0x%08X" % (((ord(t[2]) << 24) | (ord(t[3]) << 16) | (ord(t[4]) << 8) | ord(t[5])),) - # Quanta specific codes below here. - # These decodes are lifted from their U-Boot codes - elif ord(t[0]) == self._TLV_CODE_QUANTA_MAGIC and len(t) == 3: - name = "Magic Number" - value = "0x%02X" % (ord(t[2])) - elif ord(t[0]) == self._TLV_CODE_QUANTA_CRC and len(t) == 4: - name = "QUANTA-CRC" - value = "0x%04X" % ((ord(t[2]) << 8) + ord(t[3])) - elif ord(t[0]) == self._TLV_CODE_QUANTA_CARD_TYPE and len(t) == 6: - name = "Card Type" - value = "0x%08X" % (((ord(t[2]) << 24) | (ord(t[3]) << 16) | (ord(t[4]) << 8) | ord(t[5])),) - elif ord(t[0]) == self._TLV_CODE_QUANTA_HW_VERSION and len(t) == 6: - name = "Hardware Version" - value = "%d.%d" % (ord(t[2]), ord(t[3])) - elif ord(t[0]) == self._TLV_CODE_QUANTA_SW_VERSION and len(t) == 6: - name = "Software Version" - value = "%d.%d.%d.%d" % ((ord(t[2]) >> 4), (ord(t[2]) & 0xF), (ord(t[3]) >> 4), (ord(t[3]) & 0xF)) - elif ord(t[0]) == self._TLV_CODE_QUANTA_MANUF_DATE and len(t) == 6: - name = "Manufacture Date" - value = "%04d/%d/%d" % (((ord(t[2]) << 8) | ord(t[3])), ord(t[4]), ord(t[5])) - elif ord(t[0]) == self._TLV_CODE_QUANTA_MODEL_NAME: - name = "Model Name" - value = t[2:2 + ord(t[1])] - else: - name = "Unknown" - value = "" - for c in t[2:2 + ord(t[1])]: - value += "0x%02X " % (ord(c),) - return "%-20s 0x%02X %3d %s" % (name, ord(t[0]), ord(t[1]), value) - - def encoder(self, I, v): - ''' - Validate and encode the string 'v' into the TLV specified by 'I'. - I[0] is the TLV code. - ''' - try: - if I[0] == self._TLV_CODE_PRODUCT_NAME or \ - I[0] == self._TLV_CODE_PART_NUMBER or \ - I[0] == self._TLV_CODE_SERIAL_NUMBER or \ - I[0] == self._TLV_CODE_LABEL_REVISION or \ - I[0] == self._TLV_CODE_PLATFORM_NAME or \ - I[0] == self._TLV_CODE_ONIE_VERSION or \ - I[0] == self._TLV_CODE_MANUF_NAME or \ - I[0] == self._TLV_CODE_VENDOR_NAME or \ - I[0] == self._TLV_CODE_DIAG_VERSION or \ - I[0] == self._TLV_CODE_SERVICE_TAG: - errstr = "A string less than 256 characters" - if len(v) > 255: - raise - value = v - elif I[0] == self._TLV_CODE_DEVICE_VERSION: - errstr = "A number between 0 and 255" - num = int(v, 0) - if num < 0 or num > 255: - raise - value = chr(num) - elif I[0] == self._TLV_CODE_MAC_SIZE: - errstr = "A number between 0 and 65535" - num = int(v, 0) - if num < 0 or num > 65535: - raise - value = chr((num >> 8) & 0xFF) + chr(num & 0xFF) - elif I[0] == self._TLV_CODE_MANUF_DATE: - errstr = 'MM/DD/YYYY HH:MM:SS' - date, time = v.split() - mo, da, yr = [int(i) for i in date.split('/')] - hr, mn, sc = [int(i) for i in time.split(':')] - if len(v) < 19 or \ - mo < 1 or mo > 12 or da < 1 or da > 31 or yr < 0 or yr > 9999 or \ - hr < 0 or hr > 23 or mn < 0 or mn > 59 or sc < 0 or sc > 59: - raise - value = v - elif I[0] == self._TLV_CODE_MAC_BASE: - errstr = 'XX:XX:XX:XX:XX:XX' - mac_digits = v.split(':') - if len(mac_digits) != 6: - raise - value = "" - for c in mac_digits: - value = value + chr(int(c, 16)) - elif I[0] == self._TLV_CODE_MANUF_COUNTRY: - errstr = 'CC, a two character ISO 3166-1 alpha-2 country code' - if len(v) < 2: - raise - value = v[0:2] - elif I[0] == self._TLV_CODE_CRC_32: - value = '' - # Disallow setting any Quanta specific codes - elif I[0] == self._TLV_CODE_QUANTA_MAGIC or \ - I[0] == self._TLV_CODE_QUANTA_CARD_TYPE or \ - I[0] == self._TLV_CODE_QUANTA_HW_VERSION or \ - I[0] == self._TLV_CODE_QUANTA_SW_VERSION or \ - I[0] == self._TLV_CODE_QUANTA_MANUF_DATE or \ - I[0] == self._TLV_CODE_QUANTA_MODEL_NAME: - raise Exception('quanta-read-only') - else: - errstr = '0xXX ... A list of space-separated hexidecimal numbers' - value = "" - for c in v.split(): - value += chr(int(c, 0)) - except Exception as inst: - if (len(inst.args) > 0) and (inst.args[0] == 'quanta-read-only'): - sys.stderr.write("Error: '" + "0x%02X" % (I[0],) + "' -- Unable to set the read-only Quanta codes.\n") - else: - sys.stderr.write("Error: '" + "0x%02X" % (I[0],) + "' correct format is " + errstr + "\n") - exit(0) - - return chr(I[0]) + chr(len(value)) + value - - def is_checksum_field(self, I): - return False - - def checksum_field_size(self): - return 4 - - def checksum_type(self): - return 'crc32' diff --git a/sonic_psu/__init__.py b/sonic_psu/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sonic_psu/psu_base.py b/sonic_psu/psu_base.py deleted file mode 100644 index 2e2204298e..0000000000 --- a/sonic_psu/psu_base.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python -# -# psu_base.py -# -# Abstract base class for implementing platform-specific -# PSU control functionality for SONiC -# - -try: - import abc -except ImportError as e: - raise ImportError (str(e) + " - required module not found") - -class PsuBase(object): - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def get_num_psus(self): - """ - Retrieves the number of PSUs supported on the device - - :return: An integer, the number of PSUs supported on the device - """ - return 0 - - @abc.abstractmethod - def get_psu_status(self, index): - """ - Retrieves the operational status of power supply unit (PSU) defined - by index 1-based - - :param index: An integer, 1-based index of the PSU of which to query status - :return: Boolean, - - True if PSU is operating properly: PSU is inserted and powered in the device - - False if PSU is faulty: PSU is inserted in the device but not powered - """ - return False - - @abc.abstractmethod - def get_psu_presence(self, index): - """ - Retrieves the presence status of power supply unit (PSU) defined - by 1-based index - - :param index: An integer, 1-based index of the PSU of which to query status - :return: Boolean, True if PSU is plugged, False if not - """ - return False - diff --git a/sonic_sfp/__init__.py b/sonic_sfp/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/sonic_sfp/bcmshell.py b/sonic_sfp/bcmshell.py deleted file mode 100644 index 63156c7538..0000000000 --- a/sonic_sfp/bcmshell.py +++ /dev/null @@ -1,496 +0,0 @@ -#! /usr/bin/python -#------------------------------------------------------------------------------- -# -# Copyright 2012 Cumulus Networks, inc all rights reserved -# -#------------------------------------------------------------------------------- -# -try: - import sys - import os - import time - import socket - import re -except ImportError, e: - raise ImportError (str(e) + "- required module not found") - -#------------------------------------------------------------------------------- -# - -class bcmshell (object): - - """Simple expect like class that connects to a BCM diag shell, exported as a - socket fd by switchd, allowing users to run commands and extract delimited - output. bcmdiag opens the socket file, flushes the socket's read side and - issues commands via bcmdiag.run(). The command output, upto the diag shell - prompt, is read from the socket and returned to the caller.""" - - version = "1.0" - - #--------------- - # - def __init__(self, keepopen=False, timeout=10, opennow=False, logfileobj=None, - socketname="/var/run/docker-syncd/sswsyncd.socket", prompt='^drivshell>\s*$'): - """Constructor: - - keepopen - indicates that switchd socket should be kept open between - uses because the invoking application has a bunch of consecutive - activity. In this use case, the socket must be explicitly closed by - bcmshell.close() to allow multiplexing of multiple applications. - - timeout - the time, in seconds, that bcmshell.run() will sit on the - domain socket wait for read input. Note that as long as the socket is - handing us data, we'll keep reading for up seconds. - - logfileobj - a file object that should log all issued commands. None - disables command logging; sys.stdout logs every command to stdout. - logfileobj is flushed after every write. - - socketname - the name of the switchd socket file. - - prompt - the diag shell prompt that delimits the end of output""" - - if type(prompt) is not str: - raise SyntaxError("bcmshell constructor prompt expects an re string") - else: - self.re_prompt = re.compile(prompt, re.MULTILINE) - self.re_connectprompt = re.compile("bcmshell\r\n" + prompt, re.MULTILINE) - - if timeout <= 0: - raise ValueError("bcmshell.timeout must be > 0") - else: - self.timeout = timeout - if timeout > 180: - sys.stderr.write("warning: bcmshell.timeout, %s, is > 180s\n" % - str(self.timeout)) - - if not os.access(socketname, os.F_OK): - raise ValueError("socket %s does not exist" % socketname) - elif not os.access(socketname, os.R_OK | os.W_OK): - raise ValueError("missing read/write permissions for %s" % socketname) - else: - self.socketname = socketname - - if logfileobj is None: - self.logfileobj = None - elif type(logfileobj) is not file: - raise TypeError("bcmshell.logfileobj must be a file object not %s" % - type(logfileobj)) - elif 'w' not in logfileobj.mode: - raise TypeError("bcmshell.logfileobj is not a writeable file object") - else: - self.logfileobj = logfileobj - - self.keepopen = keepopen - self.socketobj = None - self.buffer = '' - - # text editing tools - # - self.re_oneline = re.compile('\n\s+') - self.re_reg_parse_raw = re.compile('^[\w()]+\.(.+)\[.*=(.*)$') - self.re_reg_parse_fields = re.compile('^[\w()]+\.(.+)\[.*\<(.*)\>') - self.re_get_field = re.compile('^(.+)=(.+)') - self.re_table_header = re.compile('^.*:[ <]+', re.MULTILINE) - self.re_table_trailer = re.compile('[ >]+$', re.MULTILINE) - self.re_conv = re.compile(r'(\d+|\D+)') - - # interface name conversion - # - self.modname = dict() - for I in range(0, 63): - self.modname['xe' + str(I)] = 'swp' + str(I) - - # open socket if required - # - if opennow and keepopen: - self.__open__() - - #--------------- - # - def __del__(self): - - """Destructor: flush and close all files that we opened""" - - try: - self.close() - except: - pass - - #--------------- - # - def __str__(self): - - """Return object state in human-readable form""" - - s = [] - s.append(repr(self)) - s.append('version: ' + self.version) - s.append('keepopen: ' + str(self.keepopen)) - s.append('timeout: ' + str(self.timeout) + ' seconds') - s.append('logfileobj: ' + str(self.logfileobj)) - s.append('socketname: ' + str(self.socketname)) - s.append('socketobj: ' + str(self.socketobj)) - s.append('prompt: \"' + str(self.re_prompt.pattern) + '\"') - s.append('buffer (last 100 chars): ' + str(self.buffer)[-100:]) - return '\n'.join(s) - - #--------------- - # - def getreg(self, reg, fields=False): - - """Get device register(s) by name. - - The general format in which registers are returned from the shell is - reg_name(N).M=V for raw values and reg_name(N).M= where N - is an array index, M is a module or port name, V is a value, and F is a - field name. Register/field values are all converted to unsigned - integers. There is a built in name converstion fucntion to change - Broadcom "xe*" port names to "swp*" names that match the kernel. - - bcmshell.getreg('reg_name') returns dict of lists of values. As an - optimization, if there is only one entry in the dict, only the list of - values is returned and if there is only one entry in each list, only the - values are returned - - Examples: - bcmshell.getreg('cmic_config') returns a value... - 1057759299. - - bcmshell.getreg('protocol_pkt_control') returns a list of values... - [0, 0, 0, ..., 0] - - bcmshell.getreg('egr_mtu') returns a dict of values.... - {'cpu0': 0x3fff, - 'xe0': 0x3fff, - ... - 'lb0': 0x3fff} - - bcmshell.getreg('pg_min_cell') returns a dict of lists of values.... - {'cpu0:'[0, 0, 0, 0, 0, 0, 0, 0], - 'xe0': [0, 0, 0, 0, 0, 0, 0, 0], - ... - 'lb0': [0, 0, 0, 0, 0, 0, 0, 0]} - - - bcmshell.getreg('reg_name', True) returns dict of lists of dicts of - field/values. The same optimizations used for raw values apply. - - Examples: - bcmshell.getreg('cmic_config', True) returns a dict of field/values... - {'IGNORE_MMU_BKP_TXDMA_PKT': 0, - 'EN_SER_INTERLEAVE_PARITY': 1, - 'MIIM_ADDR_MAP_ENABLE': 1, - 'IGNORE_MMU_BKP_REMOTE_PKT': 0, - ... - 'DMA_GARBAGE_COLLECT_EN': 0} - - bcmshell.getreg('protocol_pkt_control', True) returns - [{'SRP_PKT_TO_CPU':0, 'SRP_FWD_ACTION':0,... 'ARP_REPLY_DROP':0}, - {'SRP_PKT_TO_CPU':0, 'SRP_FWD_ACTION':0,... 'ARP_REPLY_DROP':0}, - ... - {'SRP_PKT_TO_CPU':0, 'SRP_FWD_ACTION':0,... 'ARP_REPLY_DROP':0}] - - bcmshell.getreg('egr_mtu', fields=True) returns a dict of dicts... - {'cpu0': {'MTU_SIZE',0x3fff, 'MTU_ENABLE',0}, - 'xe0': {'MTU_SIZE',0x3fff, 'MTU_ENABLE',0}, - ... - 'lb0': {'MTU_SIZE',0x3fff, 'MTU_ENABLE',0}} - - bcmshell.getreg('pg_min_cell') returns a dict of lists of values.... - {'cpu0:'[{'PG_MIN':0}, {'PG_MIN':0},... {'PG_MIN':0}], - 'xe0:'[{'PG_MIN':0}, {'PG_MIN':0},... {'PG_MIN':0}], - ... - 'lb0:'[{'PG_MIN':0}, {'PG_MIN':0},... {'PG_MIN':0}]} - """ - - # make sure everything is sane and read the register - # - if type(reg) is not str: - raise TypeError("expecting string argument to bmcdiag.getreg(reg)") - elif reg.find('\n') >= 0: - raise ValueError("unexpected newline in bmcdiag.getreg(%s)" % reg ) - elif reg.find('\s') >= 0: - raise ValueError("unexpected whitespace in bmcdiag.getreg(%s)" % reg) - - if fields: - t = self.run('getreg ' + reg) - else: - t = self.run('getreg raw ' + reg) - - if 'Syntax error parsing' in t: - raise RuntimeError('\"%s\" is not a register' % reg) - - # get the results into a list - # - t = self.re_oneline.sub('', t) - t = t.split('\n') - if t[-1] is '': - t.pop() - - # get the results into a dict (module) of lists (array) of values/fields - # - def __parse_reg__(text, fields=False): - if fields: - m = self.re_reg_parse_fields.search(text) - s = m.group(2).split(',') - t = dict([self.__get_field__(S) for S in s]) - else: - m = self.re_reg_parse_raw.search(text) - t = int(m.group(2), 16) - return(self.modname.get(m.group(1), m.group(1)), t) - - t = [__parse_reg__(T, fields) for T in t] - d = dict() - for I in t: - if I[0] in d: - d[I[0]].append(I[1]) - else: - d[I[0]] = [I[1]] - - # now optimize the return - # - for I in iter(d): - if len(d[I]) is 1: - d[I] = d[I][0] - - if len(d) is 1: - return d.values()[0] - else: - return d - - - #--------------- - # - def gettable(self, table, fields=False, start=None, entries=None): - - """Get device memory based table(s) by name. Tables are returned as a - list of value/field-dict. Table entry/field values are converted into - unsigned integers. If "fields" is set to True, we return a list of - dictionaries of field/value. - - Examples: - bcmshell.gettable('egr_ing_port') returns - [0, 0, 0, ... 0] - - bcmshell.gettable('egr_ing_port', True) returns - [{'HIGIG2': 0, 'PORT_TYPE': 0}, - {'HIGIG2': 0, 'PORT_TYPE': 0}, - {'HIGIG2': 0, 'PORT_TYPE': 0}, - ... - {'HIGIG2': 0, 'PORT_TYPE': 0}] - """ - - if type(table) is not str: - raise TypeError("bcmshell.gettable(table) expects string not %s" % - type(table)) - elif table.find('\n') >= 0: - raise ValueError("unexpected newline in bmcshell.gettable(%s)" % - table ) - elif table.find('\s') >= 0: - raise ValueError("unexpected whitespace in bmcshell.gettable(%s)" % - table) - - cmd = 'dump all' - if not fields: - cmd += ' raw' - cmd += " %s" % table - if start != None or entries != None: - cmd += " %d" % (start or 0) - cmd += " %d" % (entries or 1) - - t = self.run(cmd) - - if 'Unknown option or memory' in t: - raise RuntimeError('\"%s\" is not a table' % table) - - if 'out of range' in t: - err = table - if start != None or entries != None: - err += " %d" % (start or 0) - err += " %d" % (entries or 1) - raise IndexError('\"%s\" table index is out of range' % err) - - # get all of the return into a list - # - t = self.re_oneline.sub('', t) - t = self.re_table_header.sub('', t) - t = self.re_table_trailer.sub('', t) - t = t.split('\n') - if t[-1] is '': - t.pop() - - # parse the contents - # - def __parse_table__(text, fields=False): - if fields: - t = text.split(',') - v = [self.__get_field__(T) for T in t] - return dict(v) - else: - t = text.split() - v = 0 - for I in range(len(t)): - v += (int(t[I], 16) << (32 * I)) - return v - t = [__parse_table__(T, fields) for T in t] - - return t - - #--------------- - # - def cmd(self, cmd): - - """Run a command and print the results""" - - s = self.run(cmd) - if 'Unknown command:' in s: - raise ValueError(s) - print s - - - #--------------- - # - def prettyprint(self, d, level=0): - - """Print the structured output generated by getreg and gettable in a - human readable format""" - - s = level * 8 * " " - if type(d) is dict: - for I in sorted(d, key=self.__name_conv__): - if type(d[I]) is int: - print "%s %30s: " % (s, I), - else: - print "%s %s:" % (s, I) - self.prettyprint(d[I], (level + 1)) - elif type(d) is list: - for I in range(len(d)): - i = "[" + str(I) + "]" - if type(d[I]) is int or type(d[I]) is long: - print "%s %10s: " % (s, i), - else: - print "%s %s:" % (s, i) - self.prettyprint(d[I], (level + 1)) - else: - print "%s" % (hex(d)) - - - #--------------- - # - def close(self): - - """Close the socket object""" - - if self.socketobj is not None: - self.socketobj.shutdown(socket.SHUT_RDWR) - self.socketobj.close() - self.socketobj = None - - #--------------- - # - def run(self, cmd): - - """Issue the command to the diag shell and collect the return data until - we detect the prompt. cmd must be a string and must not include a - newline, i.e. we expect a single command to be run per call.""" - - if type(cmd) is not str: - raise TypeError("expecting string argument to bmcdiag.run(cmd)") - elif cmd.find('\n') >= 0: - raise ValueError("unexpected newline in bmcdiag.run(cmd)") - - self.__open__() - try: - self.socketobj.sendall(cmd + '\n') - except socket.error as (errno, errstr): - raise IOError("unable to send command \"%s\", %s" % (cmd, errstr)) - - self.buffer = '' - self.socketobj.settimeout(self.timeout) - quitting_time = time.time() + self.timeout - while True: - try: - self.buffer += self.socketobj.recv(4096) - except socket.timeout: - raise RuntimeError("recv stalled for %d seconds" % self.timeout) - found = self.re_prompt.search(self.buffer) - if found: - break - if time.time() > quitting_time: - raise RuntimeError("accepting input for %d seconds" % self.timeout) - - if found.end(0) != len(self.buffer): - raise RuntimeError("prompt detected in the middle of input") - - if not self.keepopen: - self.close() - return self.buffer[:found.start(0)] - - #--------------- - # - def __name_conv__(self, s): - l = self.re_conv.findall(s) - for I in range(len(l)): - if l[I].isdigit(): - l[I] = int(l[I]) - return l - - #--------------- - # - def __get_field__(self, text): - m = self.re_get_field.search(text) - return (m.group(1), int(m.group(2), 16)) - - #--------------- - # - def __open__(self): - - """Open the bcm diag shell socket exported by switchd. Complete any - dangling input by issuing a newline and flush the read side in case the - last user left something lying around. No-op if the socket is already - open. NOTE: socket.connect is non-blocking, so we need to exchange - a command with the bcm diag shell to know that we've actually obtained - socket ownership.""" - - if self.socketobj is None: - timeout = self.timeout - while True: - try: - self.socketobj = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - self.socketobj.settimeout(self.timeout) - self.socketobj.connect(self.socketname) - except socket.error as (errno, errstr): - if timeout == 0: - raise IOError("unable to open %s, %s" % (self.socketname, errstr)) - time.sleep(1) - timeout -= 1 - else: - break; - - - # flush out the socket in case it was left dirty - try: - self.socketobj.sendall('echo bcmshell\n') - quitting_time = time.time() + self.timeout - buf = '' - while True: - try: - buf += self.socketobj.recv(1024) - except socket.timeout: - raise IOError("unable to receive data from %s for %d seconds" % - (self.socketname, self.timeout)) - - found = self.re_connectprompt.search(buf) - if found: - break - if time.time() > quitting_time: - raise IOError("unable to flush %s for %d seconds" % - (self.socketname, self.timeout)) - - except socket.error as (errno, errstr): - raise IOError("Socket error: unable to flush %s on open: %s" % (self.socketname, errstr)) - except IOError as e: - raise IOError("unable to flush %s on open: %s" % (self.socketname, e.message)) - except: - raise IOError("unable to flush %s on open" % self.socketname) diff --git a/sonic_sfp/sff8436.py b/sonic_sfp/sff8436.py deleted file mode 100644 index 503ad21299..0000000000 --- a/sonic_sfp/sff8436.py +++ /dev/null @@ -1,908 +0,0 @@ -#! /usr/bin/env python -#---------------------------------------------------------------------------- -# SFF-8436 QSFP+ 10 Gbs 4X PLUGGABLE TRANSCEIVER -#---------------------------------------------------------------------------- - -try: - import fcntl - import struct - import sys - import time - import binascii - import os - import getopt - import types - from math import log10 - from sffbase import sffbase -except ImportError, e: - raise ImportError (str(e) + "- required module not found") - -class sff8436InterfaceId(sffbase): - - version = '1.0' - - specification_compliance = {'10/40G Ethernet Compliance Code': - {'offset':3, - 'size':1, - 'type' : 'bitmap', - 'decode' : { - '10GBase-LRM': - {'offset': 3, - 'bit': 6}, - '10GBase-LR': - {'offset': 3, - 'bit': 5}, - '10GBase-SR': - {'offset': 3, - 'bit': 4}, - '40GBASE-CR4': - {'offset': 3, - 'bit': 3}, - '40GBASE-SR4': - {'offset': 3, - 'bit': 2}, - '40GBASE-LR4': - {'offset': 3, - 'bit': 1}, - '40G Active Cable (XLPPI)': - {'offset': 3, - 'bit': 0}}}, - 'SONET Compliance codes': - {'offset':4, - 'size':1, - 'type' : 'bitmap', - 'decode' : { - '40G OTN (OTU3B/OTU3C)': - {'offset': 4, - 'bit': 3}, - 'OC 48, long reach': - {'offset': 4, - 'bit': 2}, - 'OC 48, intermediate reach': - {'offset': 4, - 'bit': 1}, - 'OC 48 short reach': - {'offset': 4, - 'bit': 0}}}, - 'SAS/SATA compliance codes': - {'offset': 5, - 'size' : 1, - 'type' : 'bitmap', - 'decode': { - 'SAS 6.0G': - {'offset': 5, - 'bit': 5}, - 'SAS 3.0G': - {'offset': 5, - 'bit': 4}}}, - 'Gigabit Ethernet Compliant codes': - {'offset': 6, - 'size' : 1, - 'type' : 'bitmap', - 'decode': { - '1000BASE-T': - {'offset': 6, - 'bit': 3}, - '1000BASE-CX': - {'offset': 6, - 'bit': 2}, - '1000BASE-LX': - {'offset': 6, - 'bit': 1}, - '1000BASE-SX': - {'offset': 6, - 'bit': 0}}}, - 'Fibre Channel link length/Transmitter Technology': - {'offset': 7, - 'size' : 2, - 'type' : 'bitmap', - 'decode': { - 'Very long distance (V)': - {'offset': 7, - 'bit': 7}, - 'Short distance (S)': - {'offset': 7, - 'bit': 6}, - 'Intermediate distance (I)': - {'offset': 7, - 'bit': 5}, - 'Long distance (L)': - {'offset': 7, - 'bit': 4}, - 'Medium (M)': - {'offset': 7, - 'bit': 3}, - 'Longwave laser (LC)': - {'offset': 7, - 'bit': 1}, - 'Electrical inter-enclosure (EL)': - {'offset': 7, - 'bit': 0}, - 'Electrical intra-enclosure': - {'offset': 8, - 'bit': 7}, - 'Shortwave laser w/o OFC (SN)': - {'offset': 8, - 'bit': 6}, - 'Shortwave laser w OFC (SL)': - {'offset': 8, - 'bit': 5}, - 'Longwave Laser (LL)': - {'offset': 8, - 'bit': 4}}}, - 'Fibre Channel transmission media': - {'offset': 8, - 'size' : 1, - 'type' : 'bitmap', - 'decode': { - 'Twin Axial Pair (TW)': - {'offset': 8, - 'bit': 7}, - 'Shielded Twisted Pair (TP)': - {'offset': 8, - 'bit': 6}, - 'Miniature Coax (MI)': - {'offset': 8, - 'bit': 5}, - 'Video Coax (TV)': - {'offset': 8, - 'bit': 4}, - 'Multi-mode 62.5m (M6)': - {'offset': 8, - 'bit': 3}, - 'Multi-mode 50m (M5)': - {'offset': 8, - 'bit': 2}, - 'Multi-mode 50um (OM3)': - {'offset': 8, - 'bit': 1}, - 'Single Mode (SM)': - {'offset': 8, - 'bit': 0}}}, - 'Fibre Channel Speed': - {'offset': 9, - 'size' : 1, - 'type' : 'bitmap', - 'decode': { - '1200 Mbytes/Sec': - {'offset': 9, - 'bit': 7}, - '800 Mbytes/Sec': - {'offset': 9, - 'bit': 6}, - '1600 Mbytes/Sec': - {'offset': 9, - 'bit': 5}, - '400 Mbytes/Sec': - {'offset': 9, - 'bit': 4}, - '200 Mbytes/Sec': - {'offset': 9, - 'bit': 2}, - '100 Mbytes/Sec': - {'offset': 9, - 'bit': 0}}}} - - type_of_transceiver = { - '00':'Unknown or unspecified', - '01':'GBIC', - '02': 'Module/connector soldered to motherboard', - '03': 'SFP', - '04': '300 pin XBI', - '05': 'XENPAK', - '06': 'XFP', - '07': 'XFF', - '08': 'XFP-E', - '09': 'XPAK', - '0a': 'X2', - '0b': 'DWDM-SFP', - '0c': 'QSFP', - '0d': 'QSFP+' - } - - ext_type_of_transceiver = {} - - connector = { - '00': 'Unknown or unspecified', - '01': 'SC', - '02': 'FC Style 1 copper connector', - '03': 'FC Style 2 copper connector', - '04': 'BNC/TNC', - '05': 'FC coax headers', - '06': 'Fiberjack', - '07': 'LC', - '08': 'MT-RJ', - '09': 'MU', - '0a': 'SG', - '0b': 'Optical Pigtail', - '0C': 'MPO', - '20': 'HSSDC II', - '21': 'Copper pigtail', - '22': 'RJ45', - '23': 'No separable connector' - } - - encoding_codes = { - '00':'Unspecified', - '01': '8B10B', - '02': '4B5B', - '03': 'NRZ', - '04': 'SONET Scrambled', - '05': '64B66B', - '06': 'Manchester' - } - - rate_identifier = {'00':'QSFP+ Rate Select Version 1'} - - interface_id = {'Identifier': - {'offset':0, - 'size':1, - 'type' : 'enum', - 'decode' : type_of_transceiver}, - 'Extended Identifier': - {'offset':1, - 'size':1, - 'type' : 'enum', - 'decode': ext_type_of_transceiver}, - 'Connector': - {'offset':2, - 'size':1, - 'type' : 'enum', - 'decode': connector}, - 'Specification compliance': - {'offset' : 3, - 'type' : 'nested', - 'decode' : specification_compliance}, - 'Encoding': - {'offset':11, - 'size':1, - 'type' : 'enum', - 'decode' : encoding_codes}, - 'Nominal Bit Rate(100Mbs)': - {'offset': 12, - 'size':1, - 'type':'int'}, - 'Extended RateSelect Compliance': - {'offset':13, - 'size':1, - 'type' : 'enum', - 'decode' : rate_identifier}, - 'Length(km)': - {'offset':14, - 'size':1, - 'type':'int'}, - 'Length OM3(2m)': - {'offset':15, - 'size':1, - 'type':'int'}, - 'Length OM2(m)': - {'offset':16, - 'size':1, - 'type':'int'}, - 'Length OM1(m)': - {'offset':17, - 'size':1, - 'type':'int'}, - 'Length Cable Assembly(m)': - {'offset':18, - 'size':1, - 'type':'int'}, - # Device Tech - 'Vendor Name': - {'offset' : 20, - 'size': 16, - 'type': 'str'}, - 'Vendor OUI': - {'offset': 37, - 'size' : 3, - 'type' : 'hex'}, - 'Vendor PN': - {'offset': 40, - 'size' : 16, - 'type' : 'str'}, - 'Vendor Rev': - {'offset': 56, - 'size' : 2, - 'type' : 'str'}, - 'Vendor SN': - {'offset': 68, - 'size' : 16, - 'type' : 'str'}, - 'Vendor Date Code(YYYY-MM-DD Lot)': - {'offset': 84, - 'size' : 8, - 'type' : 'date'}, - 'Diagnostic Monitoring Type': - {'offset': 92, - 'size' : 1, - 'type' : 'bitmap', - 'decode': {}}, - 'Enhanced Options': - {'offset': 93, - 'size' : 1, - 'type' : 'bitmap', - 'decode': {}}} - - - def __init__(self, eeprom_raw_data=None): - self.interface_data = None - start_pos = 128 - - if eeprom_raw_data != None: - self.interface_data = sffbase.parse(self, - self.interface_id, - eeprom_raw_data, - start_pos) - - def parse(self, eeprom_raw_data, start_pos): - return sffbase.parse(self, self.interface_id, eeprom_raw_data, start_pos) - - def dump_pretty(self): - if self.interface_data == None: - print 'Object not initialized, nothing to print' - return - sffbase.dump_pretty(self, self.interface_data) - - def get_calibration_type(self): - return self.calibration_type - - def get_data(self): - return self.interface_data - - def get_data_pretty(self): - return sffbase.get_data_pretty(self, self.interface_data) - - -class sff8436Dom(sffbase): - - version = '1.0' - - def get_calibration_type(self): - return self._calibration_type - - def calc_temperature(self, eeprom_data, offset, size): - try: - cal_type = self.get_calibration_type() - - msb = int(eeprom_data[offset], 16) - lsb = int(eeprom_data[offset + 1], 16) - - result = (msb << 8) | (lsb & 0xff) - result = self.twos_comp(result, 16) - - if cal_type == 1: - - # Internal calibration - - result = float(result / 256.0) - retval = '%.4f' %result + 'C' - elif cal_type == 2: - - # External calibration - - # T(C) = T_Slope * T_AD + T_Offset - off = self.dom_ext_calibration_constants['T_Slope']['offset'] - msb_t = int(eeprom_data[off], 16) - lsb_t = int(eeprom_data[off + 1], 16) - t_slope = (msb_t << 8) | (lsb_t & 0xff) - - off = self.dom_ext_calibration_constants['T_Offset']['offset'] - msb_t = int(eeprom_data[off], 16) - lsb_t = int(eeprom_data[off + 1], 16) - t_offset = (msb_t << 8) | (lsb_t & 0xff) - t_offset = self.twos_comp(t_offset, 16) - - result = t_slope * result + t_offset - result = float(result / 256.0) - retval = '%.4f' %result + 'C' - else: - retval = 'Unknown' - except Exception, err: - retval = str(err) - - return retval - - - def calc_voltage(self, eeprom_data, offset, size): - try: - cal_type = self.get_calibration_type() - - msb = int(eeprom_data[offset], 16) - lsb = int(eeprom_data[offset + 1], 16) - result = (msb << 8) | (lsb & 0xff) - - if cal_type == 1: - - # Internal Calibration - - result = float(result * 0.0001) - #print indent, name, ' : %.4f' %result, 'Volts' - retval = '%.4f' %result + 'Volts' - elif cal_type == 2: - - # External Calibration - - # V(uV) = V_Slope * VAD + V_Offset - off = self.dom_ext_calibration_constants['V_Slope']['offset'] - msb_v = int(eeprom_data[off], 16) - lsb_v = int(eeprom_data[off + 1], 16) - v_slope = (msb_v << 8) | (lsb_v & 0xff) - - off = self.dom_ext_calibration_constants['V_Offset']['offset'] - msb_v = int(eeprom_data[off], 16) - lsb_v = int(eeprom_data[off + 1], 16) - v_offset = (msb_v << 8) | (lsb_v & 0xff) - v_offset = self.twos_comp(v_offset, 16) - - result = v_slope * result + v_offset - result = float(result * 0.0001) - #print indent, name, ' : %.4f' %result, 'Volts' - retval = '%.4f' %result + 'Volts' - else: - #print indent, name, ' : Unknown' - retval = 'Unknown' - except Exception, err: - retval = str(err) - - return retval - - - def calc_bias(self, eeprom_data, offset, size): - try: - cal_type = self.get_calibration_type() - - msb = int(eeprom_data[offset], 16) - lsb = int(eeprom_data[offset + 1], 16) - result = (msb << 8) | (lsb & 0xff) - - if cal_type == 1: - # Internal Calibration - - result = float(result * 0.002) - #print indent, name, ' : %.4f' %result, 'mA' - retval = '%.4f' %result + 'mA' - - elif cal_type == 2: - # External Calibration - - # I(uA) = I_Slope * I_AD + I_Offset - off = self.dom_ext_calibration_constants['I_Slope']['offset'] - msb_i = int(eeprom_data[off], 16) - lsb_i = int(eeprom_data[off + 1], 16) - i_slope = (msb_i << 8) | (lsb_i & 0xff) - - off = self.dom_ext_calibration_constants['I_Offset']['offset'] - msb_i = int(eeprom_data[off], 16) - lsb_i = int(eeprom_data[off + 1], 16) - i_offset = (msb_i << 8) | (lsb_i & 0xff) - i_offset = self.twos_comp(i_offset, 16) - - result = i_slope * result + i_offset - result = float(result * 0.002) - #print indent, name, ' : %.4f' %result, 'mA' - retval = '%.4f' %result + 'mA' - else: - retval = 'Unknown' - except Exception, err: - retval = str(err) - - return retval - - - def calc_tx_power(self, eeprom_data, offset, size): - try: - cal_type = self.get_calibration_type() - - msb = int(eeprom_data[offset], 16) - lsb = int(eeprom_data[offset + 1], 16) - result = (msb << 8) | (lsb & 0xff) - - if cal_type == 1: - - result = float(result * 0.0001) - #print indent, name, ' : ', power_in_dbm_str(result) - retval = self.power_in_dbm_str(result) - - elif cal_type == 2: - - # TX_PWR(uW) = TX_PWR_Slope * TX_PWR_AD + TX_PWR_Offset - off = self.dom_ext_calibration_constants['TX_PWR_Slope']['offset'] - msb_tx_pwr = int(eeprom_data[off], 16) - lsb_tx_pwr = int(eeprom_data[off + 1], 16) - tx_pwr_slope = (msb_tx_pwr << 8) | (lsb_tx_pwr & 0xff) - - off = self.dom_ext_calibration_constants['TX_PWR_Offset']['offset'] - msb_tx_pwr = int(eeprom_data[off], 16) - lsb_tx_pwr = int(eeprom_data[off + 1], 16) - tx_pwr_offset = (msb_tx_pwr << 8) | (lsb_tx_pwr & 0xff) - tx_pwr_offset = self.twos_comp(tx_pwr_offset, 16) - - result = tx_pwr_slope * result + tx_pwr_offset - result = float(result * 0.0001) - retval = self.power_in_dbm_str(result) - else: - retval = 'Unknown' - except Exception, err: - retval = str(err) - - return retval - - - def calc_rx_power(self, eeprom_data, offset, size): - try: - cal_type = self.get_calibration_type() - - msb = int(eeprom_data[offset], 16) - lsb = int(eeprom_data[offset + 1], 16) - result = (msb << 8) | (lsb & 0xff) - - if cal_type == 1: - - # Internal Calibration - result = float(result * 0.0001) - #print indent, name, " : ", power_in_dbm_str(result) - retval = self.power_in_dbm_str(result) - - elif cal_type == 2: - - # External Calibration - - # RX_PWR(uW) = RX_PWR_4 * RX_PWR_AD + - # RX_PWR_3 * RX_PWR_AD + - # RX_PWR_2 * RX_PWR_AD + - # RX_PWR_1 * RX_PWR_AD + - # RX_PWR(0) - off = self.dom_ext_calibration_constants['RX_PWR_4']['offset'] - rx_pwr_byte3 = int(eeprom_data[off], 16) - rx_pwr_byte2 = int(eeprom_data[off + 1], 16) - rx_pwr_byte1 = int(eeprom_data[off + 2], 16) - rx_pwr_byte0 = int(eeprom_data[off + 3], 16) - rx_pwr_4 = (rx_pwr_byte3 << 24) | (rx_pwr_byte2 << 16) | (rx_pwr_byte1 << 8) | (rx_pwr_byte0 & 0xff) - - off = self.dom_ext_calibration_constants['RX_PWR_3']['offset'] - rx_pwr_byte3 = int(eeprom_data[off], 16) - rx_pwr_byte2 = int(eeprom_data[off + 1], 16) - rx_pwr_byte1 = int(eeprom_data[off + 2], 16) - rx_pwr_byte0 = int(eeprom_data[off + 3], 16) - rx_pwr_3 = (rx_pwr_byte3 << 24) | (rx_pwr_byte2 << 16) | (rx_pwr_byte1 << 8) | (rx_pwr_byte0 & 0xff) - - off = self.dom_ext_calibration_constants['RX_PWR_2']['offset'] - rx_pwr_byte3 = int(eeprom_data[off], 16) - rx_pwr_byte2 = int(eeprom_data[off + 1], 16) - rx_pwr_byte1 = int(eeprom_data[off + 2], 16) - rx_pwr_byte0 = int(eeprom_data[off + 3], 16) - rx_pwr_2 = (rx_pwr_byte3 << 24) | (rx_pwr_byte2 << 16) | (rx_pwr_byte1 << 8) | (rx_pwr_byte0 & 0xff) - - off = self.dom_ext_calibration_constants['RX_PWR_1']['offset'] - rx_pwr_byte3 = int(eeprom_data[off], 16) - rx_pwr_byte2 = int(eeprom_data[off + 1], 16) - rx_pwr_byte1 = int(eeprom_data[off + 2], 16) - rx_pwr_byte0 = int(eeprom_data[off + 3], 16) - rx_pwr_1 = (rx_pwr_byte3 << 24) | (rx_pwr_byte2 << 16) | (rx_pwr_byte1 << 8) | (rx_pwr_byte0 & 0xff) - - off = self.dom_ext_calibration_constants['RX_PWR_0']['offset'] - rx_pwr_byte3 = int(eeprom_data[off], 16) - rx_pwr_byte2 = int(eeprom_data[off + 1], 16) - rx_pwr_byte1 = int(eeprom_data[off + 2], 16) - rx_pwr_byte0 = int(eeprom_data[off + 3], 16) - rx_pwr_0 = (rx_pwr_byte3 << 24) | (rx_pwr_byte2 << 16) | (rx_pwr_byte1 << 8) | (rx_pwr_byte0 & 0xff) - - rx_pwr = (rx_pwr_4 * result) + (rx_pwr_3 * result) + (rx_pwr_2 * result) + (rx_pwr_1 * result) + rx_pwr_0 - - result = float(result * 0.0001) - #print indent, name, " : ", power_in_dbm_str(result) - retval = self.power_in_dbm_str(result) - else: - retval = 'Unknown' - except Exception, err: - retval = str(err) - - return retval - - - dom_status_indicator = {'DataNotReady': - {'offset': 2, - 'bit': 0, - 'type': 'bitvalue'}} - - dom_channel_status = {'Tx4LOS': - {'offset': 3, - 'bit': 7, - 'type': 'bitvalue'}, - 'Tx3LOS': - {'offset': 3, - 'bit': 6, - 'type': 'bitvalue'}, - 'Tx2LOS': - {'offset': 3, - 'bit': 5, - 'type': 'bitvalue'}, - 'Tx1LOS': - {'offset': 3, - 'bit': 4, - 'type': 'bitvalue'}, - 'Rx4LOS': - {'offset': 3, - 'bit': 3, - 'type': 'bitvalue'}, - 'Rx3LOS': - {'offset': 3, - 'bit': 2, - 'type': 'bitvalue'}, - 'Rx2LOS': - {'offset': 3, - 'bit': 1, - 'type': 'bitvalue'}, - 'Rx1LOS': - {'offset': 3, - 'bit': 0, - 'type': 'bitvalue'}, - 'Tx4Fault': - {'offset': 4, - 'bit': 3, - 'type': 'bitvalue'}, - 'Tx3Fault': - {'offset': 4, - 'bit': 2, - 'type': 'bitvalue'}, - 'Tx2Fault': - {'offset': 4, - 'bit': 1, - 'type': 'bitvalue'}, - 'Tx1Fault': - {'offset': 4, - 'bit': 0, - 'type': 'bitvalue'}} - - dom_module_monitor = {'TempHighAlarm': - {'offset': 6, - 'bit': 7, - 'type': 'bitvalue'}, - 'TempLowAlarm': - {'offset': 6, - 'bit': 6, - 'type': 'bitvalue'}, - 'TempHighWarning': - {'offset': 6, - 'bit': 5, - 'type': 'bitvalue'}, - 'TempLowWarning': - {'offset': 6, - 'bit': 4, - 'type': 'bitvalue'}, - 'VccHighAlarm': - {'offset': 7, - 'bit': 7, - 'type': 'bitvalue'}, - 'VccLowAlarm': - {'offset': 7, - 'bit': 6, - 'type': 'bitvalue'}, - 'VccHighWarning': - {'offset': 7, - 'bit': 5, - 'type': 'bitvalue'}, - 'VccLowWarning': - {'offset': 7, - 'bit': 4, - 'type': 'bitvalue'}} - - dom_channel_monitor = {'Rx1PowerHighAlarm': - {'offset': 9, - 'bit': 7, - 'type': 'bitvalue'}, - 'Rx1PowerLowAlarm': - {'offset': 9, - 'bit': 6, - 'type': 'bitvalue'}, - 'Rx1PowerHighWarning': - {'offset': 9, - 'bit': 5, - 'type': 'bitvalue'}, - 'Rx1PowerLowWarning': - {'offset': 9, - 'bit': 4, - 'type': 'bitvalue'}, - 'Rx2PowerHighAlarm': - {'offset': 9, - 'bit': 3, - 'type': 'bitvalue'}, - 'Rx2PowerLowAlarm': - {'offset': 9, - 'bit': 2, - 'type': 'bitvalue'}, - 'Rx2PowerHighWarning': - {'offset': 9, - 'bit': 1, - 'type': 'bitvalue'}, - 'Rx2PowerLowWarning': - {'offset': 9, - 'bit': 0, - 'type': 'bitvalue'}, - 'Rx3PowerHighAlarm': - {'offset': 10, - 'bit': 7, - 'type': 'bitvalue'}, - 'Rx3PowerLowAlarm': - {'offset': 10, - 'bit': 6, - 'type': 'bitvalue'}, - 'Rx3PowerHighWarning': - {'offset': 10, - 'bit': 5, - 'type': 'bitvalue'}, - 'Rx3PowerLowWarning': - {'offset': 10, - 'bit': 4, - 'type': 'bitvalue'}, - 'Rx4PowerHighAlarm': - {'offset': 10, - 'bit': 3, - 'type': 'bitvalue'}, - 'Rx4PowerLowAlarm': - {'offset': 10, - 'bit': 2, - 'type': 'bitvalue'}, - 'Rx4PowerHighWarning': - {'offset': 10, - 'bit': 1, - 'type': 'bitvalue'}, - 'Rx4PowerLowWarning': - {'offset': 10, - 'bit': 0, - 'type': 'bitvalue'}, - 'Tx1BiasHighAlarm': - {'offset': 11, - 'bit': 7, - 'type': 'bitvalue'}, - 'Tx1BiasLowAlarm': - {'offset': 11, - 'bit': 6, - 'type': 'bitvalue'}, - 'Tx1BiasHighWarning': - {'offset': 11, - 'bit': 5, - 'type': 'bitvalue'}, - 'Tx1BiasLowWarning': - {'offset': 11, - 'bit': 4, - 'type': 'bitvalue'}, - 'Tx2BiasHighAlarm': - {'offset': 11, - 'bit': 3, - 'type': 'bitvalue'}, - 'Tx2BiasLowAlarm': - {'offset': 11, - 'bit': 2, - 'type': 'bitvalue'}, - 'Tx2BiasHighWarning': - {'offset': 11, - 'bit': 1, - 'type': 'bitvalue'}, - 'Tx2BiasLowWarning': - {'offset': 11, - 'bit': 0, - 'type': 'bitvalue'}, - 'Tx3BiasHighAlarm': - {'offset': 12, - 'bit': 7, - 'type': 'bitvalue'}, - 'Tx3BiasLowAlarm': - {'offset': 12, - 'bit': 6, - 'type': 'bitvalue'}, - 'Tx3BiasHighWarning': - {'offset': 12, - 'bit': 5, - 'type': 'bitvalue'}, - 'Tx3BiasLowWarning': - {'offset': 12, - 'bit': 4, - 'type': 'bitvalue'}, - 'Tx4BiasHighAlarm': - {'offset': 12, - 'bit': 3, - 'type': 'bitvalue'}, - 'Tx4BiasLowAlarm': - {'offset': 12, - 'bit': 2, - 'type': 'bitvalue'}, - 'Tx4BiasHighWarning': - {'offset': 12, - 'bit': 1, - 'type': 'bitvalue'}, - 'Tx4BiasLowWarning': - {'offset': 12, - 'bit': 0, - 'type': 'bitvalue'}} - - - dom_module_monitor_values = {'Temperature': - {'offset':22, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_temperature}}, - 'Vcc': - {'offset':26, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_voltage}}} - - dom_channel_monitor_values = { - 'RX1Power': - {'offset':34, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_rx_power}}, - 'RX2Power': - {'offset':36, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_rx_power}}, - 'RX3Power': - {'offset':38, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_rx_power}}, - 'RX4Power': - {'offset':40, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_rx_power}}, - 'TX1Bias': - {'offset':42, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_bias}}, - 'TX2Bias': - {'offset':44, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_bias}}, - 'TX3Bias': - {'offset':46, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_bias}}, - 'TX4Bias': - {'offset':48, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_bias}}} - - dom_map = { - 'ModuleMonitorValues': - {'offset': 7, - 'size': 2, - 'type': 'nested', - 'decode': dom_module_monitor_values}, - 'ChannelMonitorValues': - {'offset': 10, - 'size': 2, - 'type': 'nested', - 'decode': dom_channel_monitor_values}} - - - def __init__(self, eeprom_raw_data=None, calibration_type=1): - self._calibration_type = calibration_type - start_pos = 0 - - if eeprom_raw_data != None: - self.dom_data = sffbase.parse(self, self.dom_map, - eeprom_raw_data, start_pos) - - def parse(self, eeprom_raw_data, start_pos): - return sffbase.parse(self, self.dom_map, eeprom_raw_data, - start_pos) - - def dump_pretty(self): - if self.dom_data == None: - print 'Object not initialized, nothing to print' - return - sffbase.dump_pretty(self, self.dom_data) - - def get_data(self): - return self.dom_data - - def get_data_pretty(self): - return sffbase.get_data_pretty(self, self.dom_data) diff --git a/sonic_sfp/sff8472.py b/sonic_sfp/sff8472.py deleted file mode 100644 index 8415cbeddc..0000000000 --- a/sonic_sfp/sff8472.py +++ /dev/null @@ -1,1050 +0,0 @@ -#! /usr/bin/env python -#-------------------------------------------------------------------------- -# -# Copyright 2012 Cumulus Networks, inc all rights reserved -# -#-------------------------------------------------------------------------- -try: - import fcntl - import struct - import sys - import time - import binascii - import os - import getopt - import types - from math import log10 - from sffbase import sffbase -except ImportError, e: - raise ImportError (str(e) + "- required module not found") - -#------------------------------------------------------------------------------ - -class sff8472InterfaceId(sffbase): - """Parser and interpreter for Two wire Interface ID Data fields - - Address A0h - - Base types: - XXX - Complete documentation - - outtype - can be used to dictate the type of output you get - Mainly used with bitmap type. - if outtype == 'allbits': - parse gives all bitnames with values - if outtype == 'onbits': - parse gives all onbits with value = None - """ - - version = '1.0' - - transceiver_codes = { '10GEthernetComplianceCode': - {'offset':3, - 'size':1, - 'type' : 'bitmap', - 'decode' : {'10G Base-ER': - {'offset': 3, - 'bit': 7}, - '10G Base-LRM': - {'offset': 3, - 'bit': 6}, - '10G Base-LR': - {'offset': 3, - 'bit': 5}, - '10G Base-SR': - {'offset': 3, - 'bit': 4}}}, - 'InfinibandComplianceCode': - {'offset':3, - 'size':1, - 'type' : 'bitmap', - 'decode' : {'1X SX': - {'offset': 3, - 'bit': 3}, - '1X LX': - {'offset': 3, - 'bit': 2}, - '1X Copper Active': - {'offset': 3, - 'bit': 1}, - '1X Copper Passive': - {'offset': 3, - 'bit': 0}}}, - 'ESCONComplianceCodes': - {'offset':4, - 'size':1, - 'type' : 'bitmap', - 'decode' : {'ESCON MMF, 1310nm LED': - {'offset': 4, - 'bit': 7}, - 'ESCON SMF, 1310nm Laser': - {'offset': 4, - 'bit': 6}}}, - 'SONETComplianceCodes': - {'offset': 4, - 'size':2, - 'type' : 'bitmap', - 'decode' : { 'OC-192, short reach': - {'offset': 4, - 'bit': 5}, - 'SONET reach specifier bit 1': - {'offset': 4, - 'bit': 4}, - 'SONET reach specifier bit 2': - {'offset': 4, - 'bit': 3}, - 'OC-48, long reach': - {'offset': 4, - 'bit': 2}, - 'OC-48, intermediate reach': - {'offset': 4, - 'bit': 1}, - 'OC-48, short reach': - {'offset': 4, - 'bit': 0}, - 'OC-12, single mode, long reach': - {'offset': 5, - 'bit': 6}, - 'OC-12, single mode, inter reach': - {'offset': 5, - 'bit': 5}, - 'OC-12, short reach': - {'offset': 5, - 'bit': 4}, - 'OC-3, single mode, long reach': - {'offset': 5, - 'bit': 2}, - 'OC-3, single mode, inter reach': - {'offset': 5, - 'bit': 1}, - 'OC-3, short reach': - {'offset': 5, - 'bit': 0}}}, - 'EthernetComplianceCodes': - {'offset': 6, - 'size':2, - 'type' : 'bitmap', - 'decode' : { - 'BASE-PX': - {'offset': 6, - 'bit': 7}, - 'BASE-BX10': - {'offset': 6, - 'bit': 6}, - '100BASE-FX': - {'offset': 6, - 'bit': 5}, - '100BASE-LX/LX10': - {'offset': 6, - 'bit': 4}, - '1000BASE-T': - {'offset': 6, - 'bit': 3}, - '1000BASE-CX': - {'offset': 6, - 'bit': 2}, - '1000BASE-LX': - {'offset': 6, - 'bit': 1}, - '1000BASE-SX': - {'offset': 6, - 'bit': 0}}}, - 'FibreChannelLinkLength': - {'offset': 7, - 'size':2, - 'type' : 'bitmap', - 'decode' : - {'very long distance (V)': - {'offset': 7, - 'bit': 7}, - 'short distance (S)': - {'offset': 7, - 'bit': 6}, - 'Intermediate distance (I)': - {'offset': 7, - 'bit': 5}, - 'Long distance (L)': - {'offset': 7, - 'bit': 4}, - 'medium distance (M)': - {'offset': 7, - 'bit': 3}}}, - 'FibreChannelTechnology': - {'offset': 7, - 'size':2, - 'type' : 'bitmap', - 'decode' : - {'Shortwave laser, linear Rx (SA)': - {'offset': 7, - 'bit': 2}, - 'Longwave Laser (LC)': - {'offset': 7, - 'bit': 1}, - 'Electrical inter-enclosure (EL)': - {'offset': 7, - 'bit': 0}, - 'Electrical intra-enclosure (EL)': - {'offset': 8, - 'bit': 7}, - 'Shortwave laser w/o OFC (SN)': - {'offset': 8, - 'bit': 6}, - 'Shortwave laser with OFC (SL)': - {'offset': 8, - 'bit': 5}, - 'Longwave laser (LL)': - {'offset': 8, - 'bit': 4}}}, - 'SFP+CableTechnology': - {'offset': 7, - 'size':2, - 'type' : 'bitmap', - 'decode' : - {'Active Cable': - {'offset': 8, - 'bit': 3}, - 'Passive Cable': - {'offset': 8, - 'bit': 2}}}, - 'FibreChannelTransmissionMedia': - {'offset': 7, - 'size':2, - 'type' : 'bitmap', - 'decode' : - {'Twin Axial Pair (TW)': - {'offset': 9, - 'bit': 7}, - 'Twisted Pair (TP)': - {'offset': 9, - 'bit': 6}, - 'Miniature Coax (MI)': - {'offset': 9, - 'bit': 5}, - 'Video Coax (TV)': - {'offset': 9, - 'bit': 4}, - 'Multimode, 62.5um (M6)': - {'offset': 9, - 'bit': 3}, - 'Multimode, 50um (M5, M5E)': - {'offset': 9, - 'bit': 2}, - 'Single Mode (SM)': - {'offset': 9, - 'bit': 0}}}, - 'FibreChannelSpeed': - {'offset': 7, - 'size':2, - 'type': 'bitmap', - 'decode' : - {'1200 MBytes/sec': - {'offset': 10, - 'bit': 7}, - '800 MBytes/sec': - {'offset': 10, - 'bit': 6}, - '1600 MBytes/sec': - {'offset': 10, - 'bit': 5}, - '400 MBytes/sec': - {'offset': 10, - 'bit': 4}, - '200 MBytes/sec': - {'offset': 10, - 'bit': 2}, - '100 MBytes/sec': - {'offset': 10, - 'bit': 0}}}} - - type_of_transceiver = {'00':'Unknown', - '01':'GBIC', - '02': 'Module soldered to motherboard', - '03': 'SFP or SFP Plus', - '04': '300 pin XBI', - '05': 'XENPAK', - '06': 'XFP', - '07': 'XFF', - '08': 'XFP-E', - '09': 'XPAK', - '0a': 'X2', - '0b': 'DWDM-SFP', - '0d': 'QSFP'} - - exttypeoftransceiver = {'00': 'GBIC def not specified', - '01':'GBIC is compliant with MOD_DEF 1', - '02':'GBIC is compliant with MOD_DEF 2', - '03':'GBIC is compliant with MOD_DEF 3', - '04':'GBIC/SFP defined by twowire interface ID', - '05':'GBIC is compliant with MOD_DEF 5', - '06':'GBIC is compliant with MOD_DEF 6', - '07':'GBIC is compliant with MOD_DEF 7'} - - connector = {'00': 'Unknown', - '01': 'SC', - '02': 'Fibre Channel Style 1 copper connector', - '03': 'Fibre Channel Style 2 copper connector', - '04': 'BNC/TNC', - '05': 'Fibre Channel coaxial headers', - '06': 'FibreJack', - '07': 'LC', - '08': 'MT-RJ', - '09': 'MU', - '0a': 'SG', - '0b': 'Optical pigtail', - '0C': 'MPO Parallel Optic', - '20': 'HSSDCII', - '21': 'CopperPigtail', - '22': 'RJ45'} - - encoding_codes = {'00':'Unspecified', - '01':'8B/10B', - '02':'4B/5B', - '03':'NRZ', - '04':'Manchester', - '05': 'SONET Scrambled', - '06':'64B/66B'} - - rate_identifier = {'00':'Unspecified', - '01':'Defined for SFF-8079 (4/2/1G Rate_Select & AS0/AS1)', - '02': 'Defined for SFF-8431 (8/4/2G Rx Rate_Select only)', - '03':'Unspecified', - '04': 'Defined for SFF-8431 (8/4/2G Tx Rate_Select only)', - '05':'Unspecified', - '06':'Defined for SFF-8431 (8/4/2G Independent Rx & Tx Rate_select)', - '07':'Unspecified', - '08': 'Defined for FC-PI-5 (16/8/4G Rx Rate_select only) High=16G only, Low=8G/4G', - '09': 'Unspecified', - '0a': 'Defined for FC-PI-5 (16/8/4G Independent Rx, Tx Rate_select) High=16G only, Low=8G/4G'} - - - interface_id = {'TypeOfTransceiver': - {'offset':0, - 'size':1, - 'type' : 'enum', - 'decode' : type_of_transceiver}, - 'ExtIdentOfTypeOfTransceiver': - {'offset':1, - 'size':1, - 'type' : 'enum', - 'outlevel' : 2, - 'decode': exttypeoftransceiver}, - 'Connector': - {'offset':2, - 'size':1, - 'type' : 'enum', - 'decode': connector}, - 'EncodingCodes': - {'offset':11, - 'size':1, - 'type' : 'enum', - 'decode' : encoding_codes}, - 'VendorName': - {'offset' : 20, - 'size' : 16, - 'type' : 'str'}, - 'VendorOUI': - {'offset':20, - 'size':3, - 'type' : 'str'}, - 'VendorPN': - {'offset':40, - 'size':16, - 'type' : 'str'}, - 'VendorSN': - {'offset':68, - 'size':16, - 'type' : 'str'}, - 'VendorRev': - {'offset':56, - 'size':4, - 'type' : 'str'}, - 'CalibrationType': - {'offset':92, - 'size':1, - 'type' : 'bitmap', - 'short_name' : 'calType', - 'decode' : {'Internally Calibrated': - {'offset': 92, - 'bit':5}, - 'Externally Calibrated': - {'offset': 92, - 'bit':4}, - }}, - 'ReceivedPowerMeasurementType': - {'offset':92, - 'size':1, - 'type' : 'bitmap', - 'decode' : {'Avg power': - {'offset': 92, - 'bit':3}, - 'OMA': - {'offset': 92, - 'bit':3, - 'value':0}}}, - 'RateIdentifier': - {'offset':13, - 'size':1, - 'type' : 'enum', - 'decode' : rate_identifier}, - 'TransceiverCodes': - {'offset' : 3, - 'type' : 'nested', - 'decode' : transceiver_codes}, - 'NominalSignallingRate(UnitsOf100Mbd)': - {'offset': 12, - 'size':1, - 'type':'int'}, - 'LengthSMFkm-UnitsOfKm': - {'offset':14, - 'size':1, - 'type':'int'}, - 'LengthSMF(UnitsOf100m)': - {'offset':15, - 'size':1, - 'type':'int'}, - 'Length50um(UnitsOf10m)': - {'offset':16, - 'size':1, - 'type':'int'}, - 'Length62.5um(UnitsOfm)': - {'offset':17, - 'size':1, - 'type':'int'}, - 'LengthCable(UnitsOfm)': - {'offset':18, - 'size':1, - 'type':'int'}, - 'LengthOM3(UnitsOf10m)': - {'offset':19, - 'size':1, - 'type':'int'}, - 'VendorDataCode(YYYY-MM-DD Lot)': - {'offset':84, - 'size':8, - 'type': 'date'}} - - # Returns calibration type - def _get_calibration_type(self, eeprom_data): - try: - data = int(eeprom_data[92], 16) - if self.test_bit(data, 5) != 0: - return 1 # internally calibrated - elif self.test_bit(data, 4) != 0: - return 2 # externally calibrated - else: - return 0 # Could not find calibration type - except: - return 0 - - def __init__(self, eeprom_raw_data=None): - self.interface_data = None - start_pos = 0 - - if eeprom_raw_data != None: - self.interface_data = sffbase.parse(self, - self.interface_id, - eeprom_raw_data, start_pos) - self.calibration_type = self._get_calibration_type( - eeprom_raw_data) - - def parse(self, eeprom_raw_data, start_pos): - return sffbase.parse(self, self.interface_id, eeprom_raw_data, start_pos) - - def dump_pretty(self): - if self.interface_data == None: - print 'Object not initialized, nothing to print' - return - sffbase.dump_pretty(self, self.interface_data) - - def get_calibration_type(self): - return self.calibration_type - - def get_data(self): - return self.interface_data - - def get_data_pretty(self): - return sffbase.get_data_pretty(self, self.interface_data) - - -class sff8472Dom(sffbase): - """Parser and interpretor for Diagnostics data fields at address A2h""" - - version = '1.0' - - dom_ext_calibration_constants = {'RX_PWR_4': - {'offset':56, - 'size':4}, - 'RX_PWR_3': - {'offset':60, - 'size':4}, - 'RX_PWR_2': - {'offset':64, - 'size':4}, - 'RX_PWR_1': - {'offset':68, - 'size':4}, - 'RX_PWR_0': - {'offset':72, - 'size':4}, - 'TX_I_Slope': - {'offset':76, - 'size':2}, - 'TX_I_Offset': - {'offset':78, - 'size':2}, - 'TX_PWR_Slope': - {'offset':80, - 'size':2}, - 'TX_PWR_Offset': - {'offset':82, - 'size':2}, - 'T_Slope': - {'offset':84, - 'size':2}, - 'T_Offset': - {'offset':86, - 'size':2}, - 'V_Slope': - {'offset':88, - 'size':2}, - 'V_Offset': - {'offset':90, - 'size':2}} - - - def get_calibration_type(self): - return self._calibration_type - - def calc_temperature(self, eeprom_data, offset, size): - try: - cal_type = self.get_calibration_type() - - msb = int(eeprom_data[offset], 16) - lsb = int(eeprom_data[offset + 1], 16) - - result = (msb << 8) | (lsb & 0xff) - result = self.twos_comp(result, 16) - - if cal_type == 1: - - # Internal calibration - - result = float(result / 256.0) - retval = '%.4f' %result + 'C' - elif cal_type == 2: - - # External calibration - - # T(C) = T_Slope * T_AD + T_Offset - off = self.dom_ext_calibration_constants['T_Slope']['offset'] - msb_t = int(eeprom_data[off], 16) - lsb_t = int(eeprom_data[off + 1], 16) - t_slope = (msb_t << 8) | (lsb_t & 0xff) - - off = self.dom_ext_calibration_constants['T_Offset']['offset'] - msb_t = int(eeprom_data[off], 16) - lsb_t = int(eeprom_data[off + 1], 16) - t_offset = (msb_t << 8) | (lsb_t & 0xff) - t_offset = self.twos_comp(t_offset, 16) - - result = t_slope * result + t_offset - result = float(result / 256.0) - retval = '%.4f' %result + 'C' - else: - retval = 'Unknown' - except Exception, err: - retval = str(err) - - return retval - - - def calc_voltage(self, eeprom_data, offset, size): - try: - cal_type = self.get_calibration_type() - - msb = int(eeprom_data[offset], 16) - lsb = int(eeprom_data[offset + 1], 16) - result = (msb << 8) | (lsb & 0xff) - - if cal_type == 1: - - # Internal Calibration - - result = float(result * 0.0001) - #print indent, name, ' : %.4f' %result, 'Volts' - retval = '%.4f' %result + 'Volts' - elif cal_type == 2: - - # External Calibration - - # V(uV) = V_Slope * VAD + V_Offset - off = self.dom_ext_calibration_constants['V_Slope']['offset'] - msb_v = int(eeprom_data[off], 16) - lsb_v = int(eeprom_data[off + 1], 16) - v_slope = (msb_v << 8) | (lsb_v & 0xff) - - off = self.dom_ext_calibration_constants['V_Offset']['offset'] - msb_v = int(eeprom_data[off], 16) - lsb_v = int(eeprom_data[off + 1], 16) - v_offset = (msb_v << 8) | (lsb_v & 0xff) - v_offset = self.twos_comp(v_offset, 16) - - result = v_slope * result + v_offset - result = float(result * 0.0001) - #print indent, name, ' : %.4f' %result, 'Volts' - retval = '%.4f' %result + 'Volts' - else: - #print indent, name, ' : Unknown' - retval = 'Unknown' - except Exception, err: - retval = str(err) - - return retval - - - def calc_bias(self, eeprom_data, offset, size): - try: - cal_type = self.get_calibration_type() - - msb = int(eeprom_data[offset], 16) - lsb = int(eeprom_data[offset + 1], 16) - result = (msb << 8) | (lsb & 0xff) - - if cal_type == 1: - # Internal Calibration - - result = float(result * 0.002) - #print indent, name, ' : %.4f' %result, 'mA' - retval = '%.4f' %result + 'mA' - - elif cal_type == 2: - # External Calibration - - # I(uA) = I_Slope * I_AD + I_Offset - off = self.dom_ext_calibration_constants['I_Slope']['offset'] - msb_i = int(eeprom_data[off], 16) - lsb_i = int(eeprom_data[off + 1], 16) - i_slope = (msb_i << 8) | (lsb_i & 0xff) - - off = self.dom_ext_calibration_constants['I_Offset']['offset'] - msb_i = int(eeprom_data[off], 16) - lsb_i = int(eeprom_data[off + 1], 16) - i_offset = (msb_i << 8) | (lsb_i & 0xff) - i_offset = self.twos_comp(i_offset, 16) - - result = i_slope * result + i_offset - result = float(result * 0.002) - #print indent, name, ' : %.4f' %result, 'mA' - retval = '%.4f' %result + 'mA' - else: - retval = 'Unknown' - except Exception, err: - retval = str(err) - - return retval - - - def calc_tx_power(self, eeprom_data, offset, size): - try: - cal_type = self.get_calibration_type() - - msb = int(eeprom_data[offset], 16) - lsb = int(eeprom_data[offset + 1], 16) - result = (msb << 8) | (lsb & 0xff) - - if cal_type == 1: - - result = float(result * 0.0001) - #print indent, name, ' : ', power_in_dbm_str(result) - retval = self.power_in_dbm_str(result) - - elif cal_type == 2: - - # TX_PWR(uW) = TX_PWR_Slope * TX_PWR_AD + TX_PWR_Offset - off = self.dom_ext_calibration_constants['TX_PWR_Slope']['offset'] - msb_tx_pwr = int(eeprom_data[off], 16) - lsb_tx_pwr = int(eeprom_data[off + 1], 16) - tx_pwr_slope = (msb_tx_pwr << 8) | (lsb_tx_pwr & 0xff) - - off = self.dom_ext_calibration_constants['TX_PWR_Offset']['offset'] - msb_tx_pwr = int(eeprom_data[off], 16) - lsb_tx_pwr = int(eeprom_data[off + 1], 16) - tx_pwr_offset = (msb_tx_pwr << 8) | (lsb_tx_pwr & 0xff) - tx_pwr_offset = self.twos_comp(tx_pwr_offset, 16) - - result = tx_pwr_slope * result + tx_pwr_offset - result = float(result * 0.0001) - retval = self.power_in_dbm_str(result) - else: - retval = 'Unknown' - except Exception, err: - retval = str(err) - - return retval - - - def calc_rx_power(self, eeprom_data, offset, size): - try: - cal_type = self.get_calibration_type() - - msb = int(eeprom_data[offset], 16) - lsb = int(eeprom_data[offset + 1], 16) - result = (msb << 8) | (lsb & 0xff) - - if cal_type == 1: - - # Internal Calibration - result = float(result * 0.0001) - #print indent, name, " : ", power_in_dbm_str(result) - retval = self.power_in_dbm_str(result) - - elif cal_type == 2: - - # External Calibration - - # RX_PWR(uW) = RX_PWR_4 * RX_PWR_AD + - # RX_PWR_3 * RX_PWR_AD + - # RX_PWR_2 * RX_PWR_AD + - # RX_PWR_1 * RX_PWR_AD + - # RX_PWR(0) - off = self.dom_ext_calibration_constants['RX_PWR_4']['offset'] - rx_pwr_byte3 = int(eeprom_data[off], 16) - rx_pwr_byte2 = int(eeprom_data[off + 1], 16) - rx_pwr_byte1 = int(eeprom_data[off + 2], 16) - rx_pwr_byte0 = int(eeprom_data[off + 3], 16) - rx_pwr_4 = (rx_pwr_byte3 << 24) | (rx_pwr_byte2 << 16) | (rx_pwr_byte1 << 8) | (rx_pwr_byte0 & 0xff) - - off = self.dom_ext_calibration_constants['RX_PWR_3']['offset'] - rx_pwr_byte3 = int(eeprom_data[off], 16) - rx_pwr_byte2 = int(eeprom_data[off + 1], 16) - rx_pwr_byte1 = int(eeprom_data[off + 2], 16) - rx_pwr_byte0 = int(eeprom_data[off + 3], 16) - rx_pwr_3 = (rx_pwr_byte3 << 24) | (rx_pwr_byte2 << 16) | (rx_pwr_byte1 << 8) | (rx_pwr_byte0 & 0xff) - - off = self.dom_ext_calibration_constants['RX_PWR_2']['offset'] - rx_pwr_byte3 = int(eeprom_data[off], 16) - rx_pwr_byte2 = int(eeprom_data[off + 1], 16) - rx_pwr_byte1 = int(eeprom_data[off + 2], 16) - rx_pwr_byte0 = int(eeprom_data[off + 3], 16) - rx_pwr_2 = (rx_pwr_byte3 << 24) | (rx_pwr_byte2 << 16) | (rx_pwr_byte1 << 8) | (rx_pwr_byte0 & 0xff) - - off = self.dom_ext_calibration_constants['RX_PWR_1']['offset'] - rx_pwr_byte3 = int(eeprom_data[off], 16) - rx_pwr_byte2 = int(eeprom_data[off + 1], 16) - rx_pwr_byte1 = int(eeprom_data[off + 2], 16) - rx_pwr_byte0 = int(eeprom_data[off + 3], 16) - rx_pwr_1 = (rx_pwr_byte3 << 24) | (rx_pwr_byte2 << 16) | (rx_pwr_byte1 << 8) | (rx_pwr_byte0 & 0xff) - - off = self.dom_ext_calibration_constants['RX_PWR_0']['offset'] - rx_pwr_byte3 = int(eeprom_data[off], 16) - rx_pwr_byte2 = int(eeprom_data[off + 1], 16) - rx_pwr_byte1 = int(eeprom_data[off + 2], 16) - rx_pwr_byte0 = int(eeprom_data[off + 3], 16) - rx_pwr_0 = (rx_pwr_byte3 << 24) | (rx_pwr_byte2 << 16) | (rx_pwr_byte1 << 8) | (rx_pwr_byte0 & 0xff) - - rx_pwr = (rx_pwr_4 * result) + (rx_pwr_3 * result) + (rx_pwr_2 * result) + (rx_pwr_1 * result) + rx_pwr_0 - - result = float(result * 0.0001) - #print indent, name, " : ", power_in_dbm_str(result) - retval = self.power_in_dbm_str(result) - else: - retval = 'Unknown' - except Exception, err: - retval = str(err) - - return retval - - - dom_aw_thresholds = { 'TempHighAlarm': - {'offset':0, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_temperature}}, - 'TempLowAlarm': - {'offset':2, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_temperature}}, - 'TempHighWarning': - {'offset':4, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_temperature}}, - 'TempLowWarning': - {'offset':6, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_temperature}}, - 'VoltageHighAlarm': - {'offset':8, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_voltage}}, - 'VoltageLowAlarm': - {'offset':10, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_voltage}}, - 'VoltageHighWarning': - {'offset':12, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_voltage}}, - 'VoltageLowWarning': - {'offset':14, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_voltage}}, - 'BiasHighAlarm': - {'offset':16, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_bias}}, - 'BiasLowAlarm': - {'offset':18, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_bias}}, - 'BiasHighWarning': - {'offset':20, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_bias}}, - 'BiasLowWarning': - {'offset':22, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_bias}}, - 'TXPowerHighAlarm': - {'offset':24, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_tx_power}}, - 'TXPowerLowAlarm': - {'offset':26, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_tx_power}}, - 'TXPowerHighWarning': - {'offset':28, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_tx_power}}, - 'TXPowerLowWarning': - {'offset':30, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_tx_power}}, - 'RXPowerHighAlarm': - {'offset':32, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_rx_power}}, - 'RXPowerLowAlarm': - {'offset':34, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_rx_power}}, - 'RXPowerHighWarning': - {'offset':36, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_rx_power}}, - 'RXPowerLowWarning': - {'offset':38, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_rx_power}}} - - dom_monitor = {'Temperature': - {'offset':96, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_temperature}}, - 'Vcc': - {'offset':98, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_voltage}}, - 'TXBias': - {'offset':100, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_bias}}, - 'TXPower': - {'offset':102, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_tx_power}}, - 'RXPower': - {'offset':104, - 'size':2, - 'type': 'func', - 'decode': { 'func':calc_rx_power}}} - - dom_status_control = { 'TXDisableState': - {'offset': 110, - 'bit': 7, - 'type': 'bitvalue'}, - 'SoftTXDisableSelect': - {'offset': 110, - 'bit': 6, - 'type': 'bitvalue'}, - 'RS1State': - {'offset': 110, - 'bit': 5, - 'type': 'bitvalue'}, - 'RateSelectState': - {'offset': 110, - 'bit': 4, - 'type': 'bitvalue'}, - 'SoftRateSelect': - {'offset': 110, - 'bit': 3, - 'type': 'bitvalue'}, - 'TXFaultState': - {'offset': 110, - 'bit': 2, - 'type': 'bitvalue'}, - 'RXLOSState': - {'offset': 110, - 'bit': 1, - 'type': 'bitvalue'}, - 'DataReadyBarState': - {'offset': 110, - 'bit': 0, - 'type': 'bitvalue'}} - - dom_alarm_flags = {'TempHighAlarm': - {'offset':112, - 'bit':7, - 'type': 'bitvalue'}, - 'TempLowAlarm': - {'offset':112, - 'bit':6, - 'type': 'bitvalue'}, - 'VccHighAlarm': - {'offset':112, - 'bit':5, - 'type': 'bitvalue'}, - 'VccLowAlarm': - {'offset':112, - 'bit':4, - 'type': 'bitvalue'}, - 'TXBiasHighAlarm': - {'offset':112, - 'bit':3, - 'type': 'bitvalue'}, - 'TXBiasLowAlarm': - {'offset':112, - 'bit':2, - 'type': 'bitvalue'}, - 'TXPowerHighAlarm': - {'offset':112, - 'bit':1, - 'type': 'bitvalue'}, - 'TXPowerLowAlarm': - {'offset':112, - 'bit':0, - 'type': 'bitvalue'}, - 'RXPowerHighAlarm': - {'offset':113, - 'bit':7, - 'type': 'bitvalue'}, - 'RXPowerLowAlarm': - {'offset':113, - 'bit':6, - 'type': 'bitvalue'}} - - dom_warning_flags = { 'TempHighWarning': - {'offset':116, - 'bit':7, - 'type': 'bitvalue'}, - 'TempLowWarning': - {'offset':116, - 'bit':6, - 'type': 'bitvalue'}, - 'VccHighWarning': - {'offset':116, - 'bit':5, - 'type': 'bitvalue'}, - 'VccLowWarning': - {'offset':116, - 'bit':4, - 'type': 'bitvalue'}, - 'TXBiasHighWarning': - {'offset':116, - 'bit':3, - 'type': 'bitvalue'}, - 'TXBiasLowWarning': - {'offset':116, - 'bit':2, - 'type': 'bitvalue'}, - 'TXPowerHighWarning': - {'offset':116, - 'bit':1, - 'type': 'bitvalue'}, - 'TXPowerLowWarning': - {'offset':116, - 'bit':0, - 'type': 'bitvalue'}, - 'RXPowerHighWarning': - {'offset':117, - 'bit':7, - 'type': 'bitvalue'}, - 'RXPowerLowWarning': - {'offset':117, - 'bit':6, - 'type': 'bitvalue'}} - - dom_map = {'AwThresholds': - {'offset' : 0, - 'size' : 40, - 'type' : 'nested', - 'decode' : dom_aw_thresholds}, - 'MonitorData': - {'offset':96, - 'size':10, - 'type' : 'nested', - 'decode': dom_monitor}, - 'StatusControl': - {'offset':110, - 'size':1, - 'type' : 'nested', - 'decode':dom_status_control}, - 'AlarmFlagStatus': - {'offset':112, - 'size':2, - 'type' : 'nested', - 'decode':dom_alarm_flags}, - 'WarningFlagStatus': - {'offset':112, - 'size':2, - 'type' : 'nested', - 'decode':dom_warning_flags}} - - - def __init__(self, eeprom_raw_data=None, calibration_type=0): - self._calibration_type = calibration_type - start_pos = 0 - - if eeprom_raw_data != None: - self.dom_data = sffbase.parse(self, self.dom_map, - eeprom_raw_data, start_pos) - - def parse(self, eeprom_raw_data, start_pos): - return sffbase.parse(self, self.dom_map, eeprom_raw_data, start_pos) - - - def dump_pretty(self): - if self.dom_data == None: - print 'Object not initialized, nothing to print' - return - sffbase.dump_pretty(self, self.dom_data) - - - def get_data(self): - return self.dom_data - - - def get_data_pretty(self): - return sffbase.get_data_pretty(self, self.dom_data) diff --git a/sonic_sfp/sffbase.py b/sonic_sfp/sffbase.py deleted file mode 100644 index 55b5a734d3..0000000000 --- a/sonic_sfp/sffbase.py +++ /dev/null @@ -1,250 +0,0 @@ -#! /usr/bin/env python -#---------------------------------------------------------------------------- -# sffbase class for sff8436 and sff8472 -#---------------------------------------------------------------------------- - -try: - import fcntl - import struct - import sys - import time - import binascii - import os - import getopt - import types - from math import log10 -except ImportError, e: - raise ImportError (str(e) + "- required module not found") - -class sffbase(object): - """Class to parse and interpret sff8436 and sff8472 spec for - diagnostically monitoring interfaces of optical transceivers""" - - _indent = '\t' - - def inc_indent(self): - self._indent += '\t' - - def dec_indent(self): - self._indent = self._indent[:-1] - - # Convert Hex to String - def convert_hex_to_string(self, arr, start, end): - try: - ret_str = '' - for n in range(start, end): - ret_str += arr[n] - return str.strip(binascii.unhexlify(ret_str)) - except Exception, err: - return str(err) - - # Convert Date to String - def convert_date_to_string(self, eeprom_data, offset, size): - try: - year_offset = 0 - month_offset = 2 - day_offset = 4 - lot_offset = 6 - - date = self.convert_hex_to_string(eeprom_data, offset, offset + size) - retval = "20"+ date[year_offset:month_offset] + "-" + \ - date[month_offset:day_offset] + "-" + \ - date[day_offset:lot_offset] + " " + \ - date[lot_offset:size] - except Exception, err: - retval = str(err) - return retval - - def test_bit(self, n, bitpos): - try: - mask = 1 << bitpos - if (n & mask) == 0: - return 0 - else: - return 1 - except: - return -1 - - def twos_comp(self, num, bits): - try: - if ((num & (1 << (bits - 1))) != 0): - num = num - (1 << bits) - return num - except: - return 0 - - def mw_to_dbm(self, mW): - if mW == 0: - return float("-inf") - elif mW < 0: - return float("NaN") - return 10. * log10(mW) - - - def power_in_dbm_str(self, mW): - return "%.4f%s" % (self.mw_to_dbm(mW), "dBm") - - # Parse sff base elements - def parse_sff_element(self, eeprom_data, eeprom_ele, start_pos): - value = None - offset = eeprom_ele.get('offset') + start_pos - size = eeprom_ele.get('size') - type = eeprom_ele.get('type') - decode = eeprom_ele.get('decode'); - - if type == 'enum': - # Get the matched value - value = decode.get(str(eeprom_data[offset]), 'Unknown') - - elif type == 'bitmap': - # Get the 'on' bitname - bitvalue_dict = {} - for bitname, bitinfo in sorted(decode.iteritems()): - bitinfo_offset = bitinfo.get('offset') + start_pos - bitinfo_pos = bitinfo.get('bit') - bitinfo_value = bitinfo.get('value') - data = int(eeprom_data[bitinfo_offset], 16) - bit_value = self.test_bit(data, bitinfo_pos) - if bitinfo_value != None: - if bit_value == bitinfo_value: - value = bitname - break - elif bit_value == 1: - value = bitname - break - - elif type == 'bitvalue': - # Get the value of the bit - bitpos = eeprom_ele.get('bit') - data = int(eeprom_data[offset], 16) - bitval = self.test_bit(data, bitpos) - value = ['Off', 'On'][bitval] - - elif type == 'func': - # Call the decode func to get the value - value = decode['func'](self, eeprom_data, - offset, size) - - elif type == 'str': - value = self.convert_hex_to_string(eeprom_data, offset, - offset + size) - - elif type == 'int': - data = int(eeprom_data[offset], 16) - if data != 0: - value = data - - elif type == 'date': - value = self.convert_date_to_string(eeprom_data, offset, - size) - - elif type == 'hex': - value = '-'.join(eeprom_data[offset:offset+size]) - - return value - - # Recursively parses sff data into dictionary - def parse_sff(self, eeprom_map, eeprom_data, start_pos): - outdict = {} - for name, meta_data in sorted(eeprom_map.iteritems()): - type = meta_data.get('type') - - # Initialize output value - value_dict = {} - value_dict['outtype'] = meta_data.get('outtype') - value_dict['short_name'] = meta_data.get('short_name') - - if type != 'nested': - data = self.parse_sff_element(eeprom_data, - meta_data, start_pos) - else: - nested_map = meta_data.get('decode') - data = self.parse_sff(nested_map, - eeprom_data, start_pos) - - if data != None: - value_dict['value'] = data - outdict[name] = value_dict - - return outdict - - - # Main sff parser function - def parse(self, eeprom_map, eeprom_data, start_pos): - """ Example Return format: - {'version': '1.0', 'data': {'Length50um(UnitsOf10m)': - {'outtype': None, 'value': 8, 'short_name': None}, - 'TransceiverCodes': {'outtype': None, 'value': - {'10GEthernetComplianceCode': {'outtype': None, 'value': - '10G Base-SR', 'short_name': None}}, 'short_name': None}, - 'ExtIdentOfTypeOfTransceiver': {'outtype': None, 'value': - 'GBIC/SFP func defined by two-wire interface ID', 'short_name': - None}, 'Length62.5um(UnitsOfm)': {'outtype': None,""" - - outdict = {} - return_dict = {} - - outdict = self.parse_sff(eeprom_map, eeprom_data, start_pos) - - return_dict['version'] = self.version - return_dict['data'] = outdict - - return return_dict - - - # Returns sff parsed data in a pretty dictionary format - def get_data_pretty_dict(self, indict): - outdict = {} - - for elem, elem_val in sorted(indict.iteritems()): - value = elem_val.get('value') - if type(value) == types.DictType: - outdict[elem] = sffbase.get_data_pretty_dict( - self, value) - else: - outdict[elem] = value - - return outdict - - def get_data_pretty(self, indata): - """Example Return format: - {'version': '1.0', 'data': {'Length50um(UnitsOf10m)': 8, - 'TransceiverCodes': {'10GEthernetComplianceCode': - '10G Base-SR'}, 'ExtIdentOfTypeOfTransceiver': 'GBIC/SFP func - defined by two-wire interface ID', 'Length62.5um(UnitsOfm)': 3, - 'VendorPN': 'FTLX8571D3BNL', 'RateIdentifier': 'Unspecified', - 'NominalSignallingRate(UnitsOf100Mbd)': 103, 'VendorOUI': ..}} - {'version': '1.0', 'data': {'AwThresholds': - {'TXPowerLowWarning': '-5.0004 dBm', 'TempHighWarning': - '88.0000C', 'RXPowerHighAlarm': '0.0000 dBm', - 'TXPowerHighAlarm': '-0.7998 dBm', 'RXPowerLowAlarm': - '-20.0000 dBm', 'RXPowerHighWarning': '-1.0002 dBm', - 'VoltageLowAlarm': '2.9000Volts'""" - - return_dict = {} - - return_dict['version'] = indata.get('version') - return_dict['data'] = self.get_data_pretty_dict(indata.get( - 'data')) - return return_dict - - # Dumps dict in pretty format - def dump_pretty(self, indict): - for elem, elem_val in sorted(indict.iteritems()): - if type(elem_val) == types.DictType: - print self._indent, elem, ': ' - self.inc_indent() - sff8472.dump_pretty(self, elem_val) - self.dec_indent() - elif type(elem_val) == types.ListType: - if len(elem_val) == 1: - print (self._indent, elem, ': ', - elem_val.pop()) - else: - print self._indent, elem, ': ' - self.inc_indent() - for e in elem_val: - print self._indent, e - self.dec_indent() - else: - print self._indent, elem, ': ', elem_val diff --git a/sonic_sfp/sfputilbase.py b/sonic_sfp/sfputilbase.py deleted file mode 100644 index d8e522b140..0000000000 --- a/sonic_sfp/sfputilbase.py +++ /dev/null @@ -1,608 +0,0 @@ -# sfputilbase.py -# -# Base class for creating platform-specific SFP transceiver interfaces for SONiC -# - -try: - import abc - import binascii - import os - import re - import bcmshell - from sonic_eeprom import eeprom_dts - from sff8472 import sff8472InterfaceId - from sff8472 import sff8472Dom - from sff8436 import sff8436InterfaceId - from sff8436 import sff8436Dom -except ImportError as e: - raise ImportError("%s - required module not found" % str(e)) - - -class SfpUtilError(Exception): - """Base class for exceptions in this module.""" - pass - - -class DeviceTreeError(SfpUtilError): - """Exception raised when unable to find SFP device attributes in the device tree.""" - def __init__(self, value): - self.value = value - - def __str__(self): - return repr(self.value) - - -class SfpUtilBase(object): - """ Abstract base class for SFP utility. This class - provides base EEPROM read attributes and methods common - to most platforms.""" - - __metaclass__ = abc.ABCMeta - - IDENTITY_EEPROM_ADDR = 0x50 - DOM_EEPROM_ADDR = 0x51 - SFP_DEVICE_TYPE = "24c02" - - # List to specify filter for sfp_ports - # Needed by platforms like dni-6448 which - # have only a subset of ports that support sfp - sfp_ports = [] - - # List of logical port names available on a system - """ ["swp1", "swp5", "swp6", "swp7", "swp8" ...] """ - logical = [] - - # dicts for easier conversions between logical, physical and bcm ports - logical_to_bcm = {} - logical_to_physical = {} - - """ - phytab_mappings stores mapping between logical, physical and bcm ports - from /var/lib/cumulus/phytab - For a normal non-ganged port: - "swp8": {"bcmport": "xe4", "physicalport": [8], "phyid": ["0xb"]} - - For a ganged 40G/4 port: - "swp1": {"bcmport": "xe0", "physicalport": [1, 2, 3, 4], "phyid": ["0x4", "0x5", "0x6", "0x7"]} - - For ganged 4x10G port: - "swp52s0": {"bcmport": "xe51", "physicalport": [52], "phyid": ["0x48"]}, - "swp52s1": {"bcmport": "xe52", "physicalport": [52], "phyid": ["0x49"]}, - "swp52s2": {"bcmport": "xe53", "physicalport": [52], "phyid": ["0x4a"]}, - "swp52s3": {"bcmport": "xe54", "physicalport": [52], "phyid": ["0x4b"]}, - """ - phytab_mappings = {} - - physical_to_logical = {} - physical_to_phyaddrs = {} - - port_to_i2cbus_mapping = None - - @abc.abstractproperty - def port_start(self): - """ Starting index of physical port range """ - pass - - @abc.abstractproperty - def port_end(self): - """ Ending index of physical port range """ - pass - - @abc.abstractproperty - def qsfp_ports(self): - """ Ending index of physical port range """ - pass - - @abc.abstractproperty - def port_to_eeprom_mapping(self): - """ Dictionary where key = physical port index (integer), - value = path to SFP EEPROM device file (string) """ - pass - - def __init__(self): - pass - - def _get_bcm_port(self, port_num): - bcm_port = None - - logical_port = self.physical_to_logical.get(port_num) - if logical_port is not None and len(logical_port) > 0: - bcm_port = self.logical_to_bcm.get(logical_port[0]) - - if bcm_port is None: - bcm_port = "xe%d" % (port_num - 1) - - return bcm_port - - def _get_port_i2c_adapter_id(self, port_num): - if len(self.port_to_i2cbus_mapping) == 0: - return -1 - - return self.port_to_i2cbus_mapping.get(port_num, -1) - - # Adds new sfp device on i2c adapter/bus via i2c bus new_device - # sysfs attribute - def _add_new_sfp_device(self, sysfs_sfp_i2c_adapter_path, devaddr): - try: - sysfs_nd_path = "%s/new_device" % sysfs_sfp_i2c_adapter_path - - # Write device address to new_device file - nd_file = open(sysfs_nd_path, "w") - nd_str = "%s %s" % (self.SFP_DEVICE_TYPE, hex(devaddr)) - nd_file.write(nd_str) - nd_file.close() - - except Exception, err: - print "Error writing to new device file: %s" % str(err) - return 1 - else: - return 0 - - # Deletes sfp device on i2c adapter/bus via i2c bus delete_device - # sysfs attribute - def _delete_sfp_device(self, sysfs_sfp_i2c_adapter_path, devaddr): - try: - sysfs_nd_path = "%s/delete_device" % sysfs_sfp_i2c_adapter_path - print devaddr > sysfs_nd_path - - # Write device address to delete_device file - nd_file = open(sysfs_nd_path, "w") - nd_file.write(devaddr) - nd_file.close() - except Exception, err: - print "Error writing to new device file: %s" % str(err) - return 1 - else: - return 0 - - # Returns 1 if SFP EEPROM found. Returns 0 otherwise - def _sfp_eeprom_present(self, sysfs_sfp_i2c_client_eeprompath, offset): - """Tries to read the eeprom file to determine if the - device/sfp is present or not. If sfp present, the read returns - valid bytes. If not, read returns error 'Connection timed out""" - - if not os.path.exists(sysfs_sfp_i2c_client_eeprompath): - return False - else: - try: - sysfsfile = open(sysfs_sfp_i2c_client_eeprompath, "rb") - sysfsfile.seek(offset) - sysfsfile.read(1) - except IOError: - return False - except: - return False - else: - return True - - # Read eeprom - def _read_eeprom_devid(self, port_num, devid, offset): - sysfs_i2c_adapter_base_path = "/sys/class/i2c-adapter" - eeprom_raw = [] - num_bytes = 256 - - for i in range(0, num_bytes): - eeprom_raw.append("0x00") - - if port_num in self.port_to_eeprom_mapping.keys(): - sysfs_sfp_i2c_client_eeprom_path = self.port_to_eeprom_mapping[port_num] - else: - sysfs_i2c_adapter_base_path = "/sys/class/i2c-adapter" - - i2c_adapter_id = self._get_port_i2c_adapter_id(port_num) - if i2c_adapter_id is None: - print "Error getting i2c bus num" - return None - - # Get i2c virtual bus path for the sfp - sysfs_sfp_i2c_adapter_path = "%s/i2c-%s" % (sysfs_i2c_adapter_base_path, - str(i2c_adapter_id)) - - # If i2c bus for port does not exist - if not os.path.exists(sysfs_sfp_i2c_adapter_path): - print "Could not find i2c bus %s. Driver not loaded?" % sysfs_sfp_i2c_adapter_path - return None - - sysfs_sfp_i2c_client_path = "%s/%s-00%s" % (sysfs_sfp_i2c_adapter_path, - str(i2c_adapter_id), - hex(devid)[-2:]) - - # If sfp device is not present on bus, Add it - if not os.path.exists(sysfs_sfp_i2c_client_path): - ret = self._add_new_sfp_device( - sysfs_sfp_i2c_adapter_path, devid) - if ret != 0: - print "Error adding sfp device" - return None - - sysfs_sfp_i2c_client_eeprom_path = "%s/eeprom" % sysfs_sfp_i2c_client_path - - if not self._sfp_eeprom_present(sysfs_sfp_i2c_client_eeprom_path, offset): - return None - - try: - sysfsfile_eeprom = open(sysfs_sfp_i2c_client_eeprom_path, "rb") - sysfsfile_eeprom.seek(offset) - raw = sysfsfile_eeprom.read(num_bytes) - except IOError: - print "Error: reading sysfs file %s" % sysfs_sfp_i2c_client_eeprom_path - return None - - try: - for n in range(0, num_bytes): - eeprom_raw[n] = hex(ord(raw[n]))[2:].zfill(2) - except: - return None - - try: - sysfsfile_eeprom.close() - except: - return 0 - - return eeprom_raw - - def _is_valid_port(self, port_num): - if port_num >= self.port_start and port_num <= self.port_end: - return True - - return False - - def read_porttab_mappings(self, porttabfile): - logical = [] - logical_to_bcm = {} - logical_to_physical = {} - physical_to_logical = {} - last_fp_port_index = 0 - last_portname = "" - first = 1 - port_pos_in_file = 0 - parse_fmt_port_config_ini = False - - try: - f = open(porttabfile) - except: - raise - - parse_fmt_port_config_ini = (os.path.basename(porttabfile) == "port_config.ini") - - # Read the porttab file and generate dicts - # with mapping for future reference. - # XXX: move the porttab - # parsing stuff to a separate module, or reuse - # if something already exists - for line in f: - line.strip() - if re.search("^#", line) is not None: - continue - - # Parsing logic for 'port_config.ini' file - if (parse_fmt_port_config_ini): - # bcm_port is not explicitly listed in port_config.ini format - # Currently we assume ports are listed in numerical order according to bcm_port - # so we use the port's position in the file (zero-based) as bcm_port - portname = line.split()[0] - - bcm_port = str(port_pos_in_file) - - if len(line.split()) == 4: - fp_port_index = int(line.split()[3]) - else: - fp_port_index = portname.split("Ethernet").pop() - fp_port_index = int(fp_port_index.split("s").pop(0))/4 - else: # Parsing logic for older 'portmap.ini' file - (portname, bcm_port) = line.split("=")[1].split(",")[:2] - - fp_port_index = portname.split("Ethernet").pop() - fp_port_index = int(fp_port_index.split("s").pop(0))/4 - - if ((len(self.sfp_ports) > 0) and (fp_port_index not in self.sfp_ports)): - continue - - if first == 1: - # Initialize last_[physical|logical]_port - # to the first valid port - last_fp_port_index = fp_port_index - last_portname = portname - first = 0 - - logical.append(portname) - - logical_to_bcm[portname] = "xe" + bcm_port - logical_to_physical[portname] = [fp_port_index] - if physical_to_logical.get(fp_port_index) is None: - physical_to_logical[fp_port_index] = [portname] - else: - physical_to_logical[fp_port_index].append( - portname) - - if (fp_port_index - last_fp_port_index) > 1: - # last port was a gang port - for p in range(last_fp_port_index+1, fp_port_index): - logical_to_physical[last_portname].append(p) - if physical_to_logical.get(p) is None: - physical_to_logical[p] = [last_portname] - else: - physical_to_logical[p].append(last_portname) - - last_fp_port_index = fp_port_index - last_portname = portname - - port_pos_in_file += 1 - - self.logical = logical - self.logical_to_bcm = logical_to_bcm - self.logical_to_physical = logical_to_physical - self.physical_to_logical = physical_to_logical - - """ - print "logical: " + self.logical - print "logical to bcm: " + self.logical_to_bcm - print "logical to physical: " + self.logical_to_physical - print "physical to logical: " + self.physical_to_logical - """ - - def read_phytab_mappings(self, phytabfile): - logical = [] - phytab_mappings = {} - physical_to_logical = {} - physical_to_phyaddrs = {} - - try: - f = open(phytabfile) - except: - raise - - # Read the phytab file and generate dicts - # with mapping for future reference. - # XXX: move the phytabfile - # parsing stuff to a separate module, or reuse - # if something already exists - for line in f: - line = line.strip() - line = re.sub(r"\s+", " ", line) - if len(line) < 4: - continue - if re.search("^#", line) is not None: - continue - (phy_addr, logical_port, bcm_port, type) = line.split(" ", 3) - - if re.match("xe", bcm_port) is None: - continue - - lport = re.findall("swp(\d+)s*(\d*)", logical_port) - if lport is not None: - lport_tuple = lport.pop() - physical_port = int(lport_tuple[0]) - else: - physical_port = logical_port.split("swp").pop() - physical_port = int(physical_port.split("s").pop(0)) - - # Some platforms have a list of physical sfp ports - # defined. If such a list exists, check to see if this - # port is blacklisted - if ((len(self.sfp_ports) > 0) and (physical_port not in self.sfp_ports)): - continue - - if logical_port not in logical: - logical.append(logical_port) - - if phytab_mappings.get(logical_port) is None: - phytab_mappings[logical_port] = {} - phytab_mappings[logical_port]['physicalport'] = [] - phytab_mappings[logical_port]['phyid'] = [] - phytab_mappings[logical_port]['type'] = type - - # If the port is 40G/4 ganged, there will be multiple - # physical ports corresponding to the logical port. - # Generate the next physical port number in the series - # and append it to the list - tmp_physical_port_list = phytab_mappings[logical_port]['physicalport'] - if (type == "40G/4" and physical_port in tmp_physical_port_list): - # Aha!...ganged port - new_physical_port = tmp_physical_port_list[-1] + 1 - else: - new_physical_port = physical_port - - if (new_physical_port not in phytab_mappings[logical_port]['physicalport']): - phytab_mappings[logical_port]['physicalport'].append(new_physical_port) - phytab_mappings[logical_port]['phyid'].append(phy_addr) - phytab_mappings[logical_port]['bcmport'] = bcm_port - - # Store in physical_to_logical dict - if physical_to_logical.get(new_physical_port) is None: - physical_to_logical[new_physical_port] = [] - physical_to_logical[new_physical_port].append(logical_port) - - # Store in physical_to_phyaddrs dict - if physical_to_phyaddrs.get(new_physical_port) is None: - physical_to_phyaddrs[new_physical_port] = [] - physical_to_phyaddrs[new_physical_port].append(phy_addr) - - self.logical = logical - self.phytab_mappings = phytab_mappings - self.physical_to_logical = physical_to_logical - self.physical_to_phyaddrs = physical_to_phyaddrs - - """ - pp = pprint.PrettyPrinter(indent=4) - pp.pprint(self.phytab_mappings) - - print "logical: " + self.logical - print "logical to bcm: " + self.logical_to_bcm - print "phytab mappings: " + self.phytab_mappings - print "physical to logical: " + self.physical_to_logical - print "physical to phyaddrs: " + self.physical_to_phyaddrs - """ - - def get_physical_to_logical(self, port_num): - """Returns list of logical ports for the given physical port""" - - return self.physical_to_logical[port_num] - - def get_logical_to_physical(self, logical_port): - """Returns list of physical ports for the given logical port""" - - return self.logical_to_physical[logical_port] - - def is_logical_port(self, port): - if port in self.logical: - return 1 - else: - return 0 - - def is_logical_port_ganged_40_by_4(self, logical_port): - physical_port_list = self.logical_to_physical[logical_port] - if len(physical_port_list) > 1: - return 1 - else: - return 0 - - def is_physical_port_ganged_40_by_4(self, port_num): - logical_port = self.get_physical_to_logical(port_num) - if logical_port is not None: - return self.is_logical_port_ganged_40_by_4(logical_port[0]) - - return 0 - - def get_physical_port_phyid(self, physical_port): - """Returns list of phyids for a physical port""" - - return self.physical_to_phyaddrs[physical_port] - - def get_40_by_4_gangport_phyid(self, logical_port): - """ Return the first ports phyid. One use case for - this is to address the gang port in single mode """ - - phyid_list = self.phytab_mappings[logical_port]['phyid'] - if phyid_list is not None: - return phyid_list[0] - - def is_valid_sfputil_port(self, port): - if port.startswith(""): - if self.is_logical_port(port): - return 1 - else: - return 0 - else: - return 0 - - def read_port_mappings(self): - if self.port_to_eeprom_mapping is None or self.port_to_i2cbus_mapping is None: - self.read_port_to_eeprom_mapping() - self.read_port_to_i2cbus_mapping() - - def read_port_to_eeprom_mapping(self): - eeprom_dev = "/sys/class/eeprom_dev" - self.port_to_eeprom_mapping = {} - for eeprom_path in [os.path.join(eeprom_dev, x) for x in os.listdir(eeprom_dev)]: - eeprom_label = open(os.path.join(eeprom_path, "label"), "r").read().strip() - if eeprom_label.startswith("port"): - port = int(eeprom_label[4:]) - self.port_to_eeprom_mapping[port] = os.path.join(eeprom_path, "device", "eeprom") - - def read_port_to_i2cbus_mapping(self): - if self.port_to_i2cbus_mapping is not None and len(self.port_to_i2cbus_mapping) > 0: - return - - self.eep_dict = eeprom_dts.get_dev_attr_from_dtb(['sfp']) - if len(self.eep_dict) == 0: - return - - # XXX: there should be a cleaner way to do this. - i2cbus_list = [] - self.port_to_i2cbus_mapping = {} - s = self.port_start - for sfp_sysfs_path, attrs in sorted(self.eep_dict.iteritems()): - i2cbus = attrs.get("dev-id") - if i2cbus is None: - raise DeviceTreeError("No 'dev-id' attribute found in attr: %s" % repr(attrs)) - if i2cbus in i2cbus_list: - continue - i2cbus_list.append(i2cbus) - self.port_to_i2cbus_mapping[s] = i2cbus - s += 1 - if s > self.port_end: - break - - def get_eeprom_raw(self, port_num): - # Read interface id EEPROM at addr 0x50 - return self._read_eeprom_devid(port_num, self.IDENTITY_EEPROM_ADDR, 0) - - def get_eeprom_dom_raw(self, port_num): - if port_num in self.qsfp_ports: - # QSFP DOM EEPROM is also at addr 0x50 and thus also stored in eeprom_ifraw - return None - else: - # Read dom eeprom at addr 0x51 - return self._read_eeprom_devid(port_num, self.DOM_EEPROM_ADDR, 0) - - def get_eeprom_dict(self, port_num): - """Returns dictionary of interface and dom data. - format: { : {'interface': {'version' : '1.0', 'data' : {...}}, - 'dom' : {'version' : '1.0', 'data' : {...}}}} - """ - - sfp_data = {} - - eeprom_ifraw = self.get_eeprom_raw(port_num) - eeprom_domraw = self.get_eeprom_dom_raw(port_num) - - if eeprom_ifraw is None: - return None - - if port_num in self.qsfp_ports: - sfpi_obj = sff8436InterfaceId(eeprom_ifraw) - if sfpi_obj is not None: - sfp_data['interface'] = sfpi_obj.get_data_pretty() - # For Qsfp's the dom data is part of eeprom_if_raw - # The first 128 bytes - - sfpd_obj = sff8436Dom(eeprom_ifraw) - if sfpd_obj is not None: - sfp_data['dom'] = sfpd_obj.get_data_pretty() - return sfp_data - - sfpi_obj = sff8472InterfaceId(eeprom_ifraw) - if sfpi_obj is not None: - sfp_data['interface'] = sfpi_obj.get_data_pretty() - cal_type = sfpi_obj.get_calibration_type() - - if eeprom_domraw is not None: - sfpd_obj = sff8472Dom(eeprom_domraw, cal_type) - if sfpd_obj is not None: - sfp_data['dom'] = sfpd_obj.get_data_pretty() - - return sfp_data - - @abc.abstractmethod - def get_presence(self, port_num): - """ - :param port_num: Integer, index of physical port - :returns: Boolean, True if tranceiver is present, False if not - """ - return - - @abc.abstractmethod - def get_low_power_mode(self, port_num): - """ - :param port_num: Integer, index of physical port - :returns: Boolean, True if low-power mode enabled, False if disabled - """ - return - - @abc.abstractmethod - def set_low_power_mode(self, port_num, lpmode): - """ - :param port_num: Integer, index of physical port - :param lpmode: Boolean, True to enable low-power mode, False to disable it - :returns: Boolean, True if low-power mode set successfully, False if not - """ - return - - @abc.abstractmethod - def reset(self, port_num): - """ - :param port_num: Integer, index of physical port - :returns: Boolean, True if reset successful, False if not - """ - return From 17e0bf9be5f16ba301f30d3354e7554b49fd61e5 Mon Sep 17 00:00:00 2001 From: Andriy Moroz Date: Tue, 16 Jan 2018 10:45:35 +0200 Subject: [PATCH 29/32] Port speed set utility (#174) * Add portconfig utility Signed-off-by: Andriy Moroz * Fix units conversion in scripts/intfutil Signed-off-by: Andriy Moroz * Remove old script Signed-off-by: Andriy Moroz --- config/main.py | 13 ++++++++ scripts/intfutil | 2 +- scripts/portconfig | 78 ++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 1 + 4 files changed, 93 insertions(+), 1 deletion(-) create mode 100755 scripts/portconfig diff --git a/config/main.py b/config/main.py index c101a445a1..ebe64169a4 100755 --- a/config/main.py +++ b/config/main.py @@ -371,6 +371,19 @@ def startup(interface_name, verbose): command = "ip link set {} up".format(interface_name) run_command(command, display_cmd=verbose) +# +# 'speed' subcommand +# + +@interface.command() +@click.argument('interface_name', metavar='', required=True) +@click.argument('interface_speed', metavar='', required=True) +@click.option('-v', '--verbose', is_flag=True, help="Enable verbose output") +def speed(interface_name, interface_speed, verbose): + """Set interface speed""" + command = "portconfig -p {} -s {}".format(interface_name, interface_speed) + if verbose: command += " -vv" + run_command(command, display_cmd=verbose) # # 'acl' group diff --git a/scripts/intfutil b/scripts/intfutil index 6c44a74fdf..07fa734b9c 100755 --- a/scripts/intfutil +++ b/scripts/intfutil @@ -54,7 +54,7 @@ def db_port_status_get(db, intf_name, status_type): return "N/A" if status_type == PORT_SPEED and status != "N/A": - status = '{}G'.format(status[:2] if int(status) < 100000 else status[:3]) + status = '{}G'.format(status[:-3]) return status diff --git a/scripts/portconfig b/scripts/portconfig new file mode 100755 index 0000000000..2505ae971c --- /dev/null +++ b/scripts/portconfig @@ -0,0 +1,78 @@ +#!/usr/bin/python +""" +portconfig is the utility to show and change ECN configuration + +usage: portconfig [-h] [-v] [-s] [-p PROFILE] [-gmin GREEN_MIN] + [-gmax GREEN_MAX] [-ymin YELLOW_MIN] [-ymax YELLOW_MAX] + [-rmin RED_MIN] [-rmax RED_MAX] [-vv] + +optional arguments: + -h --help show this help message and exit + -v --version show program's version number and exit + -vv --verbose verbose output + -p' --port port name + -s --speed port speed in Mbits +""" +from __future__ import print_function + +import os +import sys +import json +import argparse +import swsssdk + +PORT_TABLE_NAME = "PORT" +PORT_SPEED_CONFIG_FIELD_NAME = "speed" + +class portconfig(object): + """ + Process aclstat + """ + def __init__(self, verbose, port): + self.verbose = verbose + + # Set up db connections + self.db = swsssdk.ConfigDBConnector() + self.db.connect() + # check whether table for this port exists + port_tables = self.db.get_table(PORT_TABLE_NAME) + if not port_tables.has_key(port): + raise Exception("Invalid port specified") + + def list_params(self, port): + # chack whether table for this port exists + port_tables = self.db.get_table(PORT_TABLE_NAME) + if port_tables.has_key(port): + print(port_tables[port]) + + def set_speed(self, port, speed): + if self.verbose: + print("Setting speed %s on port %s" % (speed, port)) + self.db.mod_entry(PORT_TABLE_NAME, port, {PORT_SPEED_CONFIG_FIELD_NAME: speed}) + +def main(): + parser = argparse.ArgumentParser(description='Set SONiC port parameters', + version='1.0.0', + formatter_class=argparse.RawTextHelpFormatter) + parser.add_argument('-p', '--port', type=str, help='port name (e.g. Ethernet0)', required=True, default=None) + parser.add_argument('-l', '--list', action='store_true', help='list port parametars', default=False) + parser.add_argument('-s', '--speed', type=int, help='port speed value in Mbit', default=None) + parser.add_argument('-vv', '--verbose', action='store_true', help='Verbose output', default=False) + args = parser.parse_args() + + try: + port = portconfig(args.verbose, args.port) + if args.list: + port.list_params(args.port) + elif args.speed: + port.set_speed(args.port, args.speed) + else: + parser.print_help() + sys.exit(1) + + except Exception as e: + print(e.message, file=sys.stderr) + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/setup.py b/setup.py index bc23ad9d29..bec645946b 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,7 @@ def get_test_suite(): 'scripts/intfutil', 'scripts/lldpshow', 'scripts/port2alias', + 'scripts/portconfig', 'scripts/portstat', 'scripts/teamshow' ], From c6bcfa66368e5dca67009893bd55c46845703363 Mon Sep 17 00:00:00 2001 From: Ying Xie Date: Thu, 18 Jan 2018 15:52:52 -0800 Subject: [PATCH 30/32] [sonic_install] sync filesystem after file copy is done (#189) * [sonic_installer] sync filesystem after file copy is done * [sonics-installer] sleep 3 seconds after executing sync --- sonic_installer/main.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sonic_installer/main.py b/sonic_installer/main.py index 697b91a65a..ffc4279bf2 100644 --- a/sonic_installer/main.py +++ b/sonic_installer/main.py @@ -189,9 +189,12 @@ def install(url): os.chmod(image_path, stat.S_IXUSR) run_command(image_path) run_command('grub-set-default --boot-directory=' + HOST_PATH + ' 0') - run_command("sync") run_command("rm -rf /host/old_config") run_command("cp -r /etc/sonic /host/old_config") + + # sync filesystem, keep at last step. + run_command("sync") + run_command("sleep 3") # wait 3 seconds after sync click.echo('Done') From 26c3709786a4072625d7f3ebb9f2e52d798a3ba0 Mon Sep 17 00:00:00 2001 From: Ying Xie Date: Thu, 18 Jan 2018 15:53:15 -0800 Subject: [PATCH 31/32] [reboot] While rebooting device, execute platform specific reboot tool when available (#188) * [fast-reboot] Explicitly call Linux native reboot tool * [reboot tool] Reboot device with platform specific tool when available /usr/bin is in front of /sbin in search path. Adding reboot script here will get executed when reboot was called without absolute path. This script will call platform specific reboot tool (name: platform_reboot under device folder) when available. Otherwise, dispatch to native Linux reboot. * [reboot] call sync before calling platform specific reboot tool The platform specific reboot tool could be power cycling. * [reboot] sleep 3 seconds after sync-ing * [reboot] also stop snmp service --- scripts/fast-reboot | 4 ++-- scripts/reboot | 28 ++++++++++++++++++++++++++++ setup.py | 1 + 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100755 scripts/reboot diff --git a/scripts/fast-reboot b/scripts/fast-reboot index 21d153e8f3..ba397e5ea0 100755 --- a/scripts/fast-reboot +++ b/scripts/fast-reboot @@ -59,6 +59,6 @@ sync sleep 1 sync -# Reboot +# Reboot: explicity call Linux native reboot under sbin echo "Rebooting to $NEXT_SONIC_IMAGE..." -reboot +/sbin/reboot diff --git a/scripts/reboot b/scripts/reboot new file mode 100755 index 0000000000..019e5ad762 --- /dev/null +++ b/scripts/reboot @@ -0,0 +1,28 @@ +#! /bin/bash + +function stop_sonic_services() +{ + echo "Stopping sonic services..." + systemctl stop swss + systemctl stop teamd + systemctl stop bgp + systemctl stop lldp + systemctl stop snmp +} + +# Obtain our platform as we will mount directories with these names in each docker +PLATFORM=`sonic-cfggen -v platform` + +DEVPATH="/usr/share/sonic/device" +REBOOT="platform_reboot" + +if [ -x ${DEVPATH}/${PLATFORM}/${REBOOT} ]; then + stop_sonic_services + sync + sleep 3 + echo "Rebooting with platform ${PLATFORM} specific tool ..." + ${DEVPATH}/${PLATFORM}/${REBOOT} + exit 0 +fi + +/sbin/reboot $@ diff --git a/setup.py b/setup.py index bec645946b..61e42fd438 100644 --- a/setup.py +++ b/setup.py @@ -49,6 +49,7 @@ def get_test_suite(): 'scripts/port2alias', 'scripts/portconfig', 'scripts/portstat', + 'scripts/reboot', 'scripts/teamshow' ], data_files=[ From 620a15c5ceda3ab386e03c59f3045059a4a18b66 Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Tue, 23 Jan 2018 14:46:01 -0800 Subject: [PATCH 32/32] [config] Update ACLs in load_minigraph if ACL config is present (#190) --- config/main.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/main.py b/config/main.py index ebe64169a4..14708b946d 100755 --- a/config/main.py +++ b/config/main.py @@ -189,6 +189,8 @@ def load_minigraph(): command = "{} -m --write-to-db".format(SONIC_CFGGEN_PATH) run_command(command, display_cmd=True) client.set(config_db.INIT_INDICATOR, True) + if os.path.isfile('/etc/sonic/acl.json'): + run_command("acl-loader update full /etc/sonic/acl.json", display_cmd=True) #FIXME: After config DB daemon is implemented, we'll no longer need to restart every service. _restart_services() print "Please note setting loaded from minigraph will be lost after system reboot. To preserve setting, run `config save`."