From 81436c36c4841b7b2d236d2ed6de23b64e7a5f4e Mon Sep 17 00:00:00 2001 From: Sergey Popovich Date: Thu, 9 Aug 2018 17:31:14 +0300 Subject: [PATCH] device: netberg: Add System EEPROM, PSU, SFP and LED util modules To provide interface between SONiC and platform hardware monitor driver add eeprom.py, led_control.py, psuutil.py and sfputil.py modules. Note that led_control.py only implements constructor and destructor to light green system stat LED when ledd starts and turn off it when ledd finishes. Signed-off-by: Sergey Popovich --- .../led_proc_init.soc | 5 +- .../plugins/eeprom.py | 21 ++ .../plugins/led_control.py | 335 ++++++++++++++++++ .../plugins/psuutil.py | 74 ++++ .../plugins/sfputil.py | 167 +++++++++ rules/docker-platform-monitor.mk | 3 + 6 files changed, 603 insertions(+), 2 deletions(-) create mode 100644 device/netberg/x86_64-netberg_aurora_420_rangeley-r0/plugins/eeprom.py create mode 100644 device/netberg/x86_64-netberg_aurora_420_rangeley-r0/plugins/led_control.py create mode 100644 device/netberg/x86_64-netberg_aurora_420_rangeley-r0/plugins/psuutil.py create mode 100644 device/netberg/x86_64-netberg_aurora_420_rangeley-r0/plugins/sfputil.py diff --git a/device/netberg/x86_64-netberg_aurora_420_rangeley-r0/led_proc_init.soc b/device/netberg/x86_64-netberg_aurora_420_rangeley-r0/led_proc_init.soc index 92083b036aa7..feca43aaec90 100644 --- a/device/netberg/x86_64-netberg_aurora_420_rangeley-r0/led_proc_init.soc +++ b/device/netberg/x86_64-netberg_aurora_420_rangeley-r0/led_proc_init.soc @@ -39,6 +39,8 @@ modreg CMIC_LEDUP0_PORT_ORDER_REMAP_52_55 REMAP_PORT_52=0 REMAP_PORT_53=0 R modreg CMIC_LEDUP0_PORT_ORDER_REMAP_56_59 REMAP_PORT_56=0 REMAP_PORT_57=0 REMAP_PORT_58=0 REMAP_PORT_59=0 modreg CMIC_LEDUP0_PORT_ORDER_REMAP_60_63 REMAP_PORT_60=0 REMAP_PORT_61=0 REMAP_PORT_62=0 REMAP_PORT_63=0 +led 0 auto off + led 1 stop led 1 prog \ 12 00 61 f1 29 67 33 74 0d 67 52 67 8b 81 86 f1 \ @@ -74,8 +76,7 @@ modreg CMIC_LEDUP1_PORT_ORDER_REMAP_52_55 REMAP_PORT_52=12 REMAP_PORT_53=11 R modreg CMIC_LEDUP1_PORT_ORDER_REMAP_56_59 REMAP_PORT_56=8 REMAP_PORT_57=7 REMAP_PORT_58=6 REMAP_PORT_59=5 modreg CMIC_LEDUP1_PORT_ORDER_REMAP_60_63 REMAP_PORT_60=4 REMAP_PORT_61=3 REMAP_PORT_62=2 REMAP_PORT_63=1 -led 0 auto on -led 1 auto on +led 1 auto off led 0 start led 1 start diff --git a/device/netberg/x86_64-netberg_aurora_420_rangeley-r0/plugins/eeprom.py b/device/netberg/x86_64-netberg_aurora_420_rangeley-r0/plugins/eeprom.py new file mode 100644 index 000000000000..c87ba6a1b0fd --- /dev/null +++ b/device/netberg/x86_64-netberg_aurora_420_rangeley-r0/plugins/eeprom.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +try: + import exceptions + import binascii + import time + import optparse + import warnings + import os + import sys + from sonic_eeprom import eeprom_base + from sonic_eeprom import eeprom_tlvinfo + import subprocess +except ImportError, e: + raise ImportError (str(e) + "- required module not found") + +class board(eeprom_tlvinfo.TlvInfoDecoder): + _TLV_INFO_MAX_LEN = 256 + def __init__(self, name, path, cpld_root, ro): + self.eeprom_path = "/sys/bus/i2c/devices/1-0070/eeprom" + super(board, self).__init__(self.eeprom_path, 0, '', True) diff --git a/device/netberg/x86_64-netberg_aurora_420_rangeley-r0/plugins/led_control.py b/device/netberg/x86_64-netberg_aurora_420_rangeley-r0/plugins/led_control.py new file mode 100644 index 000000000000..3cc44437a534 --- /dev/null +++ b/device/netberg/x86_64-netberg_aurora_420_rangeley-r0/plugins/led_control.py @@ -0,0 +1,335 @@ +#!/usr/bin/env python +# +# led_control.py +# +# Platform-specific LED control functionality for SONiC +# + +try: + from sonic_led.led_control_base import LedControlBase + import os.path + import time + import socket + import swsssdk +except ImportError, e: + raise ImportError (str(e) + " - required module not found") + + +class LedControl(LedControlBase): + """Platform specific LED control class""" + + # SYSTEM(STAT) LED + SYSTEM_LED_PATH = "/sys/bus/i2c/devices/1-0070/system_led" + + SYSTEM_LED_OFF = 0 + SYSTEM_LED_AMBER = 1 + SYSTEM_LED_GREEN = 2 + + # PORT LED + SYNCD_SOCK_PATH = "/var/run/sswsyncd/sswsyncd.socket" + + PORT_TABLE_PREFIX = "PORT_TABLE:" + PORT_NAME_PREFIX = "Ethernet" + + LED_COLOR_OFF = 0x00 + LED_COLOR_GREEN = 0x10 + LED_COLOR_YELLOW = 0x01 + + _port_start = 0 + _port_end = 71 + _qsfp_port_start = 48 + _qsfp_port_end = 71 + + # n: [c, o, s] + # + # n - Internal port number (0,71) + # c - LED controller (0,1) + # o - Data Ram offset in CMIC_LEDUPc_DATA_RAM(o) + # s - Port state (0 - off, 1 - on) + # + _port_to_led_mapping = { + # CMIC_LEDUP0_DATA_RAM + 0: [0, 160, 0], + 1: [0, 161, 0], + 2: [0, 162, 0], + 3: [0, 163, 0], + 4: [0, 164, 0], + 5: [0, 165, 0], + 6: [0, 166, 0], + 7: [0, 167, 0], + 8: [0, 168, 0], + 9: [0, 169, 0], + 10: [0, 170, 0], + 11: [0, 171, 0], + 12: [0, 172, 0], + 13: [0, 173, 0], + 14: [0, 174, 0], + 15: [0, 175, 0], + 16: [0, 176, 0], + 17: [0, 177, 0], + 18: [0, 178, 0], + 19: [0, 179, 0], + 20: [0, 180, 0], + 21: [0, 181, 0], + 22: [0, 182, 0], + 23: [0, 183, 0], + 24: [0, 184, 0], + 25: [0, 185, 0], + 26: [0, 186, 0], + 27: [0, 187, 0], + 28: [0, 188, 0], + 29: [0, 189, 0], + 30: [0, 190, 0], + 31: [0, 191, 0], + 32: [0, 192, 0], + 33: [0, 193, 0], + 34: [0, 194, 0], + 35: [0, 195, 0], + # CMIC_LEDUP1_DATA_RAM + 36: [1, 160, 0], + 37: [1, 161, 0], + 38: [1, 162, 0], + 39: [1, 163, 0], + 40: [1, 164, 0], + 41: [1, 165, 0], + 42: [1, 166, 0], + 43: [1, 167, 0], + 44: [1, 168, 0], + 45: [1, 169, 0], + 46: [1, 170, 0], + 47: [1, 171, 0], + 48: [1, 172, 0],#QSFP49 + 49: [1, 172, 0], + 50: [1, 172, 0], + 51: [1, 172, 0], + 52: [1, 176, 0],#QSFP50 + 53: [1, 176, 0], + 54: [1, 176, 0], + 55: [1, 176, 0], + 56: [1, 180, 0],#QSFP51 + 57: [1, 180, 0], + 58: [1, 180, 0], + 59: [1, 180, 0], + 60: [1, 184, 0],#QSFP52 + 61: [1, 184, 0], + 62: [1, 184, 0], + 63: [1, 184, 0], + 64: [1, 188, 0],#QSFP53 + 65: [1, 188, 0], + 66: [1, 188, 0], + 67: [1, 188, 0], + 68: [1, 192, 0],#QSFP54 + 69: [1, 192, 0], + 70: [1, 192, 0], + 71: [1, 192, 0], + } + + _qsfp_nr_sfp_up = { + 172: 0,#QSFP49 + 176: 0,#QSFP50 + 180: 0,#QSFP51 + 184: 0,#QSFP52 + 188: 0,#QSFP53 + 192: 0,#QSFP54 + } + + swss = None + + @property + def port_start(self): + return self._port_start + + @property + def port_end(self): + return self._port_end + + @property + def qsfp_port_start(self): + return self._qsfp_port_start + + @property + def qsfp_port_end(self): + return self._qsfp_port_end + + @property + def qsfp_ports(self): + return range(self._qsfp_port_start, self._qsfp_port_end + 1) + + # Concrete implementation of port_link_state_change() method + def port_link_state_change(self, port, state): + # Strip "Ethernet" off port name + if not port.startswith(self.PORT_NAME_PREFIX): + return + + port_num = int(port[len(self.PORT_NAME_PREFIX):]) + + # Check for invalid port_num + if port_num < self.port_start or port_num > self.port_end: + return + + # Check for port state change + ostate = self._port_to_led_mapping[port_num][2] + nstate = int(state == "up") + if nstate == ostate: + return + self._port_to_led_mapping[port_num][2] = nstate + + if port_num < self.qsfp_port_start: + if nstate: + speed = self.get_port_speed(port) + if speed is None or speed < 10000: + color = self.LED_COLOR_YELLOW + else: + color = self.LED_COLOR_GREEN + else: + color = self.LED_COLOR_OFF + else: + x = self._port_to_led_mapping[port_num][1] + + old_nr_sfp_up = self._qsfp_nr_sfp_up[x] + old_color = self.qsfp_port_led_color(old_nr_sfp_up) + + nr_sfp_up = old_nr_sfp_up - (ostate - nstate) + color = self.qsfp_port_led_color(nr_sfp_up) + + self._qsfp_nr_sfp_up[x] = nr_sfp_up + + if color == old_color: + return + + # Create one time socket to prevent blocking + # access to drivshell for other users (e.g. bcmcmd) + sock = None + try: + # Setup non-blocking socket + sock = socket.socket(socket.AF_UNIX) + sock.settimeout(1) + sock.connect(self.SYNCD_SOCK_PATH) + + # Send setreg command + setreg = self.get_port_setreg(port_num, color) + sock.sendall(setreg) + + # Read back reply (e.g. command echo and drivshell>) + sock.recv(1024) + except: + pass + finally: + del sock + + # Get QSFP port color + def qsfp_port_led_color(self, nr_sfp_up): + if nr_sfp_up >= 4: + return self.LED_COLOR_GREEN + elif nr_sfp_up > 0: + return self.LED_COLOR_YELLOW + else: + return self.LED_COLOR_OFF + + # Get drivshell "setreg CMIC_LEDUPc_DATA_RAM(o) v" command + def get_port_setreg(self, port_num, color): + return "\nsetreg CMIC_LEDUP{:d}_DATA_RAM({:d}) {:#x}\n".format( + self._port_to_led_mapping[port_num][0], + self._port_to_led_mapping[port_num][1], + color + ) + + # Routines to get physical port speed from APPL_DB + def get_port_speed(self, port_name): + port_name = self.PORT_TABLE_PREFIX + port_name + for i in range(0, 2): + try: + speed = self.swss.get(self.swss.APPL_DB, port_name, 'speed') + except: + self.fini_swsssdk() + if i > 0: + return None + self.init_swsssdk() + if self.swss is None: + return None + else: + return int(speed) + + def init_swsssdk(self): + try: + self.swss = swsssdk.SonicV2Connector() + self.swss.connect(self.swss.APPL_DB) + except: + self.swss = None + + def fini_swsssdk(self): + try: + self.swss.close(self.swss.APPL_DB) + del self.swss + except: + return + finally: + self.swss = None + + # Initialize with color or turn off all port LEDs + # optionally waiting indefinitely for socket + def config_port_leds(self, color = LED_COLOR_OFF, wait_for_sock = False): + sock = None + while True: + try: + sock = socket.socket(socket.AF_UNIX) + sock.settimeout(1) + sock.connect(self.SYNCD_SOCK_PATH) + except: + if not wait_for_sock: + return False + while not os.path.exists(self.SYNCD_SOCK_PATH): + time.sleep(1) + else: + try: + for port_num in self._port_to_led_mapping: + setreg = self.get_port_setreg(port_num, self.LED_COLOR_OFF) + sock.sendall(setreg) + sock.recv(1024) + except: + pass + else: + return True + finally: + # Do this explicitly to ensure we close soket before retry + del sock + + # Initialize with color or turn off status LED + # optionally waiting indefinitely for hardware monitor (hwmon) sysfs path + def config_system_led(self, color = SYSTEM_LED_OFF, wait_for_hwmon = False): + while True: + try: + with open(self.SYSTEM_LED_PATH, 'w') as f: + f.write(str(color)) + except: + if not wait_for_hwmon: + return False + while not os.path.exists(self.SYSTEM_LED_PATH): + time.sleep(1) + else: + return True + + # Constructor + def __init__(self): + # Initialize front-panel status LED to amber + self.config_system_led(color = self.SYSTEM_LED_AMBER, wait_for_hwmon = True) + + # Initialize swss APPL_DB communication + self.init_swsssdk() + + # Turn off port LEDs + self.config_port_leds(wait_for_sock = True) + + # Initialize front-panel status LED to green + self.config_system_led(color = self.SYSTEM_LED_GREEN, wait_for_hwmon = True) + + # Destructor + def __del__(self): + # Turn off front-panel status LED + self.config_system_led() + + # Turn off port LEDs + self.config_port_leds() + + # Finalize swss APPL_DB communication + self.fini_swsssdk() diff --git a/device/netberg/x86_64-netberg_aurora_420_rangeley-r0/plugins/psuutil.py b/device/netberg/x86_64-netberg_aurora_420_rangeley-r0/plugins/psuutil.py new file mode 100644 index 000000000000..98b00cac9057 --- /dev/null +++ b/device/netberg/x86_64-netberg_aurora_420_rangeley-r0/plugins/psuutil.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python + +############################################################################# +# Netberg +# +# Module contains an implementation of SONiC PSU Base API and +# provides the PSUs status which are available in the platform +# +############################################################################# + +import os.path + +try: + from sonic_psu.psu_base import PsuBase +except ImportError as e: + raise ImportError (str(e) + "- required module not found") + +class PsuUtil(PsuBase): + """Platform-specific PSUutil class""" + + def __init__(self): + PsuBase.__init__(self) + + self.psu_path = "/sys/bus/i2c/devices/0-002f/" + self.psu_presence = "psu{}_abs" + self.psu_oper_status = "psu{}_pg" + + def get_num_psus(self): + """ + Retrieves the number of PSUs available on the device + + :return: An integer, the number of PSUs available on the device + """ + return 2 + + def get_psu_status(self, index): + """ + Retrieves the oprational 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 operating properly, False if PSU is faulty + """ + if index is None: + return False + + status = 0 + try: + with open(self.psu_path + self.psu_oper_status.format(index), 'r') as power_status: + status = int(power_status.read()) + except IOError: + return False + + return status == 1 + + 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 + """ + if index is None: + return False + + status = 0 + try: + with open(self.psu_path + self.psu_presence.format(index), 'r') as presence_status: + status = int(presence_status.read()) + except IOError: + return False + + return status == 1 diff --git a/device/netberg/x86_64-netberg_aurora_420_rangeley-r0/plugins/sfputil.py b/device/netberg/x86_64-netberg_aurora_420_rangeley-r0/plugins/sfputil.py new file mode 100644 index 000000000000..f50f8fdf8713 --- /dev/null +++ b/device/netberg/x86_64-netberg_aurora_420_rangeley-r0/plugins/sfputil.py @@ -0,0 +1,167 @@ +# sfputil.py +# +# Platform-specific SFP transceiver interface for SONiC +# + +try: + import time + from sonic_sfp.sfputilbase import SfpUtilBase +except ImportError as e: + raise ImportError("%s - required module not found" % str(e)) + + +class SfpUtil(SfpUtilBase): + """Platform-specific SfpUtil class""" + + BASE_PATH = "/sys/bus/i2c/devices/1-0070/port_{:03d}/" + + _port_start = 0 + _port_end = 71 + _qsfp_port_start = 48 + _qsfp_port_end = 71 + + _port_to_eeprom_mapping = {} + + _port_to_i2c_mapping = { + 0: 1, + 1: 2, + 2: 3, + 3: 4, + 4: 5, + 5: 6, + 6: 7, + 7: 8, + 8: 9, + 9: 10, + 10: 11, + 11: 12, + 12: 13, + 13: 14, + 14: 15, + 15: 16, + 16: 17, + 17: 18, + 18: 19, + 19: 20, + 20: 21, + 21: 22, + 22: 23, + 23: 24, + 24: 25, + 25: 26, + 26: 27, + 27: 28, + 28: 29, + 29: 30, + 30: 31, + 31: 32, + 32: 33, + 33: 34, + 34: 35, + 35: 36, + 36: 37, + 37: 38, + 38: 39, + 39: 40, + 40: 41, + 41: 42, + 42: 43, + 43: 44, + 44: 45, + 45: 46, + 46: 47, + 47: 48, + 48: 49, #QSFP49 + 49: 49, + 50: 49, + 51: 49, + 52: 50, #QSFP50 + 53: 50, + 54: 50, + 55: 50, + 56: 51, #QSFP51 + 57: 51, + 58: 51, + 59: 51, + 60: 52, #QSFP52 + 61: 52, + 62: 52, + 63: 52, + 64: 53, #QSFP53 + 65: 53, + 66: 53, + 67: 53, + 68: 54, #QSFP54 + 69: 54, + 70: 54, + 71: 54, + } + + @property + def port_start(self): + return self._port_start + + @property + def port_end(self): + return self._port_end + + @property + def qsfp_port_start(self): + return self._qsfp_port_start + + @property + def qsfp_port_end(self): + return self._qsfp_port_end + + @property + def qsfp_ports(self): + return range(self._qsfp_port_start, self._qsfp_port_end + 1) + + @property + def port_to_eeprom_mapping(self): + return self._port_to_eeprom_mapping + + def __init__(self): + eeprom_path = self.BASE_PATH + "data_a0" + + for x in range(0, self.port_end + 1): + self.port_to_eeprom_mapping[x] = eeprom_path.format( + self._port_to_i2c_mapping[x] + ) + + SfpUtilBase.__init__(self) + + def get_presence(self, port_num): + # Check for invalid port_num + if port_num < self.port_start or port_num > self.port_end: + return False + + present_path = self.BASE_PATH + "abs" + + port_to_is_present = present_path.format( + self._port_to_i2c_mapping[port_num] + ) + + try: + val_file = open(port_to_is_present) + except IOError as e: + print "Error: unable to open file: %s" % str(e) + return False + + content = val_file.readline().rstrip() + val_file.close() + + # content is a string, either "0" or "1" + return content == "1" + + def get_low_power_mode(self, port_num): + raise NotImplementedError + + def set_low_power_mode(self, port_num, lpmode): + raise NotImplementedError + + def reset(self, port_num): + raise NotImplementedError + + def get_transceiver_change_event(self, timeout=0): + raise NotImplementedError diff --git a/rules/docker-platform-monitor.mk b/rules/docker-platform-monitor.mk index d6404e18c47f..af989da3e8bc 100644 --- a/rules/docker-platform-monitor.mk +++ b/rules/docker-platform-monitor.mk @@ -17,4 +17,7 @@ $(DOCKER_PLATFORM_MONITOR)_RUN_OPT += -v /etc/sonic:/etc/sonic:ro # Mount Arista python library on Aboot images to be used by plugins $(DOCKER_PLATFORM_MONITOR)_aboot_RUN_OPT += -v /usr/lib/python2.7/dist-packages/arista:/usr/lib/python2.7/dist-packages/arista:ro +# Mount syncd socket to be able to use drivshell +$(DOCKER_PLATFORM_MONITOR)_RUN_OPT += -v /var/run/docker-syncd:/var/run/sswsyncd:ro + $(DOCKER_PLATFORM_MONITOR)_BASE_IMAGE_FILES += sensors:/usr/bin/sensors