Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into master-thermalctl…
Browse files Browse the repository at this point in the history
…d-interval
  • Loading branch information
andywongarista committed Jun 30, 2021
2 parents 91ba5fc + 2d2749a commit 20be710
Show file tree
Hide file tree
Showing 32 changed files with 1,160 additions and 283 deletions.
2 changes: 2 additions & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ jobs:
sudo pip2 install sonic_platform_common-1.0-py2-none-any.whl
sudo pip3 install swsssdk-2.0.1-py3-none-any.whl
sudo pip3 install sonic_py_common-1.0-py3-none-any.whl
sudo pip3 install sonic_yang_mgmt-1.0-py3-none-any.whl
sudo pip3 install sonic_yang_models-1.0-py3-none-any.whl
sudo pip3 install sonic_config_engine-1.0-py3-none-any.whl
sudo pip3 install sonic_platform_common-1.0-py3-none-any.whl
workingDirectory: $(Pipeline.Workspace)/target/python-wheels/
Expand Down
105 changes: 105 additions & 0 deletions sonic-chassisd/scripts/chassis_db_init
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/env python3

"""
chassis_db_init
Chassis information update tool for SONiC
This tool runs one time at the launch of the platform monitor in order to populate STATE_DB with chassis information such as model, serial number, and revision.
"""

try:
import os
import sys

from sonic_py_common import daemon_base, logger

# If unit testing is occurring, mock swsscommon and module_base
if os.getenv("CHASSIS_DB_INIT_UNIT_TESTING") == "1":
from tests import mock_swsscommon as swsscommon
else:
from swsscommon import swsscommon
except ImportError as e:
raise ImportError(str(e) + " - required module not found")

#
# Constants
#

SYSLOG_IDENTIFIER = "chassis_db_init"

CHASSIS_INFO_TABLE = 'CHASSIS_INFO'
CHASSIS_INFO_KEY_TEMPLATE = 'chassis {}'
CHASSIS_INFO_CARD_NUM_FIELD = 'module_num'
CHASSIS_INFO_SERIAL_FIELD = 'serial'
CHASSIS_INFO_MODEL_FIELD = 'model'
CHASSIS_INFO_REV_FIELD = 'revision'

CHASSIS_LOAD_ERROR = 1

NOT_AVAILABLE = 'N/A'

#
# Helper functions =============================================================
#

# try get information from platform API and return a default value if caught NotImplementedError


def try_get(callback, *args, **kwargs):
"""
Handy function to invoke the callback and catch NotImplementedError
:param callback: Callback to be invoked
:param args: Arguments to be passed to callback
:param kwargs: Default return value if exception occur
:return: Default return value if exception occur else return value of the callback
"""
default = kwargs.get('default', NOT_AVAILABLE)
try:
ret = callback(*args)
if ret is None:
ret = default
except NotImplementedError:
ret = default

return ret

#
# Functions
#

def provision_db(platform_chassis, log):
# Init state db connection
state_db = daemon_base.db_connect("STATE_DB")
chassis_table = swsscommon.Table(state_db, CHASSIS_INFO_TABLE)

# Populate DB with chassis hardware info
fvs = swsscommon.FieldValuePairs([
(CHASSIS_INFO_SERIAL_FIELD, try_get(platform_chassis.get_serial)),
(CHASSIS_INFO_MODEL_FIELD, try_get(platform_chassis.get_model)),
(CHASSIS_INFO_REV_FIELD, try_get(platform_chassis.get_revision))
])
chassis_table.set(CHASSIS_INFO_KEY_TEMPLATE.format(1), fvs)
log.log_info("STATE_DB provisioned with chassis info.")

return chassis_table


#
# Main
#

def main():
log = logger.Logger(SYSLOG_IDENTIFIER)
log.log_info("Provisioning Database with Chassis Info...")

# Load platform api class
try:
import sonic_platform.platform
platform_chassis = sonic_platform.platform.Platform().get_chassis()
except Exception as e:
log.log_error("Failed to load chassis due to {}".format(repr(e)))
sys.exit(CHASSIS_LOAD_ERROR)

provision_db(platform_chassis, log)

if __name__ == '__main__':
main()
42 changes: 41 additions & 1 deletion sonic-chassisd/scripts/chassisd
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ CHASSIS_MODULE_INFO_NAME_FIELD = 'name'
CHASSIS_MODULE_INFO_DESC_FIELD = 'desc'
CHASSIS_MODULE_INFO_SLOT_FIELD = 'slot'
CHASSIS_MODULE_INFO_OPERSTATUS_FIELD = 'oper_status'
CHASSIS_MODULE_INFO_NUM_ASICS_FIELD = 'num_asics'
CHASSIS_MODULE_INFO_ASICS = 'asics'

CHASSIS_ASIC_INFO_TABLE = 'CHASSIS_ASIC_TABLE'
CHASSIS_ASIC = 'asic'
CHASSIS_ASIC_PCI_ADDRESS_FIELD = 'asic_pci_address'
CHASSIS_ASIC_ID_IN_MODULE_FIELD = 'asic_id_in_module'

CHASSIS_MIDPLANE_INFO_TABLE = 'CHASSIS_MIDPLANE_TABLE'
CHASSIS_MIDPLANE_INFO_KEY_TEMPLATE = 'CHASSIS_MIDPLANE {}'
Expand Down Expand Up @@ -163,6 +170,9 @@ class ModuleUpdater(logger.Logger):
CHASSIS_MODULE_INFO_SLOT_FIELD,
CHASSIS_MODULE_INFO_OPERSTATUS_FIELD]

chassis_state_db = daemon_base.db_connect("CHASSIS_STATE_DB")
self.asic_table = swsscommon.Table(chassis_state_db, CHASSIS_ASIC_INFO_TABLE)

self.midplane_initialized = try_get(chassis.init_midplane_switch, default=False)
if not self.midplane_initialized:
self.log_error("Chassisd midplane intialization failed")
Expand All @@ -182,6 +192,11 @@ class ModuleUpdater(logger.Logger):
if self.chassis_table is not None:
self.chassis_table._del(CHASSIS_INFO_KEY_TEMPLATE.format(1))

if self.asic_table is not None:
asics = list(self.asic_table.getKeys())
for asic in asics:
self.asic_table._del(asic)

def modules_num_update(self):
# Check if module list is populated
num_modules = self.chassis.get_num_modules()
Expand All @@ -194,6 +209,8 @@ class ModuleUpdater(logger.Logger):
self.chassis_table.set(CHASSIS_INFO_KEY_TEMPLATE.format(1), fvs)

def module_db_update(self):
notOnlineModules = []

for module_index in range(0, self.num_modules):
module_info_dict = self._get_module_info(module_index)
if module_info_dict is not None:
Expand All @@ -211,9 +228,29 @@ class ModuleUpdater(logger.Logger):
fvs = swsscommon.FieldValuePairs([(CHASSIS_MODULE_INFO_DESC_FIELD, module_info_dict[CHASSIS_MODULE_INFO_DESC_FIELD]),
(CHASSIS_MODULE_INFO_SLOT_FIELD,
module_info_dict[CHASSIS_MODULE_INFO_SLOT_FIELD]),
(CHASSIS_MODULE_INFO_OPERSTATUS_FIELD, module_info_dict[CHASSIS_MODULE_INFO_OPERSTATUS_FIELD])])
(CHASSIS_MODULE_INFO_OPERSTATUS_FIELD, module_info_dict[CHASSIS_MODULE_INFO_OPERSTATUS_FIELD]),
(CHASSIS_MODULE_INFO_NUM_ASICS_FIELD, str(len(module_info_dict[CHASSIS_MODULE_INFO_ASICS])))])
self.module_table.set(key, fvs)

if module_info_dict[CHASSIS_MODULE_INFO_OPERSTATUS_FIELD] != str(ModuleBase.MODULE_STATUS_ONLINE):
notOnlineModules.append(key)
continue

for asic_id, asic in enumerate(module_info_dict[CHASSIS_MODULE_INFO_ASICS]):
asic_global_id, asic_pci_addr = asic
asic_key = "%s%s" % (CHASSIS_ASIC, asic_global_id)
asic_fvs = swsscommon.FieldValuePairs([(CHASSIS_ASIC_PCI_ADDRESS_FIELD, asic_pci_addr),
(CHASSIS_MODULE_INFO_NAME_FIELD, key),
(CHASSIS_ASIC_ID_IN_MODULE_FIELD, str(asic_id))])
self.asic_table.set(asic_key, asic_fvs)

# Asics that are on the "not online" modules need to be cleaned up
asics = list(self.asic_table.getKeys())
for asic in asics:
fvs = self.asic_table.get(asic)
if fvs[CHASSIS_MODULE_INFO_NAME_FIELD] in notOnlineModules:
self.asic_table._del(asic)

def _get_module_info(self, module_index):
"""
Retrieves module info of this module
Expand All @@ -225,11 +262,14 @@ class ModuleUpdater(logger.Logger):
slot = try_get(self.chassis.get_module(module_index).get_slot, default=INVALID_SLOT)
status = try_get(self.chassis.get_module(module_index).get_oper_status,
default=ModuleBase.MODULE_STATUS_OFFLINE)
asics = try_get(self.chassis.get_module(module_index).get_all_asics,
default=[])

module_info_dict[CHASSIS_MODULE_INFO_NAME_FIELD] = name
module_info_dict[CHASSIS_MODULE_INFO_DESC_FIELD] = str(desc)
module_info_dict[CHASSIS_MODULE_INFO_SLOT_FIELD] = str(slot)
module_info_dict[CHASSIS_MODULE_INFO_OPERSTATUS_FIELD] = str(status)
module_info_dict[CHASSIS_MODULE_INFO_ASICS] = asics

return module_info_dict

Expand Down
1 change: 1 addition & 0 deletions sonic-chassisd/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
],
scripts=[
'scripts/chassisd',
'scripts/chassis_db_init'
],
setup_requires=[
'pytest-runner',
Expand Down
18 changes: 16 additions & 2 deletions sonic-chassisd/tests/mock_platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ def get_serial(self):


class MockModule(MockDevice):
def __init__(self, module_index, module_name, module_desc, module_type, module_slot):
def __init__(self, module_index, module_name, module_desc, module_type, module_slot,
asic_list=[]):
self.module_index = module_index
self.module_name = module_name
self.module_desc = module_desc
Expand All @@ -29,7 +30,8 @@ def __init__(self, module_index, module_name, module_desc, module_type, module_s
self.admin_state = 1
self.supervisor_slot = 16
self.midplane_access = False

self.asic_list = asic_list

def get_name(self):
return self.module_name

Expand Down Expand Up @@ -69,6 +71,9 @@ def is_midplane_reachable(self):
def set_midplane_reachable(self, up):
self.midplane_access = up

def get_all_asics(self):
return self.asic_list

class MockChassis:
def __init__(self):
self.module_list = []
Expand All @@ -92,3 +97,12 @@ def get_module_index(self, module_name):

def init_midplane_switch(self):
return True

def get_serial(self):
return "Serial No"

def get_model(self):
return "Model A"

def get_revision(self):
return "Rev C"
3 changes: 3 additions & 0 deletions sonic-chassisd/tests/mock_swsscommon.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ def get(self, key):
return self.mock_dict[key]
return None

def getKeys(self):
return list(self.mock_dict)

def size(self):
return len(self.mock_dict)

Expand Down
38 changes: 38 additions & 0 deletions sonic-chassisd/tests/test_chassis_db_init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import os
import sys
from imp import load_source

from mock import Mock, MagicMock, patch
from sonic_py_common import daemon_base

from .mock_platform import MockChassis, MockModule
from .mock_module_base import ModuleBase

SYSLOG_IDENTIFIER = 'chassis_db_init_test'
NOT_AVAILABLE = 'N/A'

daemon_base.db_connect = MagicMock()

test_path = os.path.dirname(os.path.abspath(__file__))
modules_path = os.path.dirname(test_path)
scripts_path = os.path.join(modules_path, "scripts")
sys.path.insert(0, modules_path)

os.environ["CHASSIS_DB_INIT_UNIT_TESTING"] = "1"
load_source('chassis_db_init', scripts_path + '/chassis_db_init')
from chassis_db_init import *


def test_provision_db():
chassis = MockChassis()
log = MagicMock()
serial = "Serial No"
model = "Model A"
revision = "Rev C"

chassis_table = provision_db(chassis, log)

fvs = chassis_table.get(CHASSIS_INFO_KEY_TEMPLATE.format(1))
assert serial == fvs[CHASSIS_INFO_SERIAL_FIELD]
assert model == fvs[CHASSIS_INFO_MODEL_FIELD]
assert revision == fvs[CHASSIS_INFO_REV_FIELD]
75 changes: 75 additions & 0 deletions sonic-chassisd/tests/test_chassisd.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
CHASSIS_INFO_KEY_TEMPLATE = 'CHASSIS {}'
CHASSIS_INFO_CARD_NUM_FIELD = 'module_num'

CHASSIS_ASIC_PCI_ADDRESS_FIELD = 'asic_pci_address'
CHASSIS_ASIC_ID_IN_MODULE_FIELD = 'asic_id_in_module'

def setup_function():
ModuleUpdater.log_notice = MagicMock()
Expand Down Expand Up @@ -366,3 +368,76 @@ def test_midplane_presence_supervisor():
module_updater.deinit()
fvs = midplane_table.get(name)
assert fvs == None

def test_asic_presence():
chassis = MockChassis()

#Supervisor
index = 0
name = "SUPERVISOR0"
desc = "Supervisor card"
slot = 16
module_type = ModuleBase.MODULE_TYPE_SUPERVISOR
supervisor = MockModule(index, name, desc, module_type, slot)
supervisor.set_midplane_ip()
chassis.module_list.append(supervisor)

#Linecard
index = 1
name = "LINE-CARD0"
desc = "36 port 400G card"
slot = 1
module_type = ModuleBase.MODULE_TYPE_LINE
module = MockModule(index, name, desc, module_type, slot)
module.set_midplane_ip()
chassis.module_list.append(module)

#Fabric-card with asics
index = 1
name = "FABRIC-CARD0"
desc = "Switch fabric card"
slot = 17
module_type = ModuleBase.MODULE_TYPE_FABRIC
fabric_asic_list = [("4", "0000:04:00.0"), ("5", "0000:05:00.0")]
fabric = MockModule(index, name, desc, module_type, slot, fabric_asic_list)
chassis.module_list.append(fabric)

#Run on supervisor
module_updater = ModuleUpdater(SYSLOG_IDENTIFIER, chassis)
module_updater.supervisor_slot = supervisor.get_slot()
module_updater.my_slot = supervisor.get_slot()
module_updater.modules_num_update()
module_updater.module_db_update()
module_updater.check_midplane_reachability()

#Asic presence on fabric module
fabric.set_oper_status(ModuleBase.MODULE_STATUS_ONLINE)
module_updater.module_db_update()
fabric_asic_table = module_updater.asic_table
assert len(fabric_asic_table.getKeys()) == 2

def verify_fabric_asic(asic_name, asic_pci_address, module_name, asic_id_in_module):
fvs = fabric_asic_table.get(asic_name)
assert fvs[CHASSIS_ASIC_PCI_ADDRESS_FIELD] == asic_pci_address
assert fvs[CHASSIS_MODULE_INFO_NAME_FIELD] == module_name
assert fvs[CHASSIS_ASIC_ID_IN_MODULE_FIELD] == asic_id_in_module

verify_fabric_asic("asic4", "0000:04:00.0", name, "0")
verify_fabric_asic("asic5", "0000:05:00.0", name, "1")

#Card goes down and asics should be gone
fabric.set_oper_status(ModuleBase.MODULE_STATUS_OFFLINE)
module_updater.module_db_update()
assert len(fabric_asic_table.getKeys()) == 0

#Deinit
fabric.set_oper_status(ModuleBase.MODULE_STATUS_ONLINE)
module_updater.module_db_update()
module_updater.deinit()
midplane_table = module_updater.midplane_table
fvs = midplane_table.get(name)
assert fvs == None
fvs = fabric_asic_table.get("asic4")
assert fvs == None
fvs = fabric_asic_table.get("asic5")
assert fvs == None
2 changes: 2 additions & 0 deletions sonic-pcied/pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pytest]
addopts = --cov=scripts --cov-report html --cov-report term --cov-report xml --junitxml=test-results.xml -vv
Loading

0 comments on commit 20be710

Please sign in to comment.