-
Notifications
You must be signed in to change notification settings - Fork 227
[xcvrd] Add Initial CPO Support #843
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4e904ca
df83f8b
5f2d9ae
d50896b
2a6c1ec
e9c8810
bb38069
91a8c52
e8509e9
8bc4f17
f413b2d
3eb3aa8
2b65686
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| from unittest.mock import MagicMock, patch | ||
|
|
||
| from xcvrd.xcvrd_utilities import common | ||
|
|
||
|
|
||
| class TestPortDeviceResolver: | ||
| def test_is_cpo_port_no_chassis(self): | ||
| with patch.object(common, 'platform_chassis', None): | ||
| assert common.is_cpo_port(0) is False | ||
|
|
||
| def test_is_cpo_port_true_when_cpo_present(self): | ||
| chassis = MagicMock() | ||
| chassis.get_cpo.return_value = MagicMock() | ||
| with patch.object(common, 'platform_chassis', chassis): | ||
| assert common.is_cpo_port(3) is True | ||
| chassis.get_cpo.assert_called_with(3) | ||
|
|
||
| def test_is_cpo_port_false_when_not_cpo(self): | ||
| chassis = MagicMock() | ||
| chassis.get_cpo.return_value = None | ||
| with patch.object(common, 'platform_chassis', chassis): | ||
| assert common.is_cpo_port(3) is False | ||
|
|
||
| def test_is_cpo_port_swallows_not_implemented(self): | ||
| chassis = MagicMock() | ||
| chassis.get_cpo.side_effect = NotImplementedError | ||
| with patch.object(common, 'platform_chassis', chassis): | ||
| assert common.is_cpo_port(3) is False | ||
|
|
||
| def test_get_port_device_prefers_cpo(self): | ||
| chassis = MagicMock() | ||
| cpo = MagicMock() | ||
| chassis.get_cpo.return_value = cpo | ||
| with patch.object(common, 'platform_chassis', chassis): | ||
| assert common.get_port_device(1) is cpo | ||
| chassis.get_sfp.assert_not_called() | ||
|
|
||
| def test_get_port_device_falls_back_to_sfp(self): | ||
| chassis = MagicMock() | ||
| sfp = MagicMock() | ||
| chassis.get_cpo.return_value = None | ||
| chassis.get_sfp.return_value = sfp | ||
| with patch.object(common, 'platform_chassis', chassis): | ||
| assert common.get_port_device(1) is sfp | ||
|
|
||
| def test_get_port_device_none_when_unavailable(self): | ||
| with patch.object(common, 'platform_chassis', None): | ||
| assert common.get_port_device(1) is None | ||
|
|
||
|
|
||
| class TestObjDictAccessors: | ||
| def _make_port_mapping(self, physical_ports=(0, 1, 2)): | ||
| port_mapping = MagicMock() | ||
| port_mapping.physical_to_logical = {p: ['Ethernet{}'.format(p * 4)] for p in physical_ports} | ||
| return port_mapping | ||
|
|
||
| def _make_obj_dict(self): | ||
| return {0: MagicMock(), 1: MagicMock(), 2: MagicMock()} | ||
|
|
||
| def test_accessors_are_complementary(self): | ||
| objs = self._make_obj_dict() | ||
| port_mapping = self._make_port_mapping() | ||
| with patch.object(common, 'is_cpo_port', side_effect=lambda p: p in (1,)), \ | ||
| patch.object(common, 'get_port_device', side_effect=lambda p: objs[p]): | ||
| cpo = common.get_cpo_obj_dict(port_mapping) | ||
| pluggable = common.get_pluggable_obj_dict(port_mapping) | ||
| assert set(cpo) | set(pluggable) == set(objs) | ||
| assert set(cpo) & set(pluggable) == set() | ||
| assert set(cpo) == {1} | ||
| assert cpo[1] is objs[1] | ||
|
|
||
| def test_all_pluggable_when_no_cpo(self): | ||
| objs = self._make_obj_dict() | ||
| port_mapping = self._make_port_mapping() | ||
| with patch.object(common, 'is_cpo_port', return_value=False), \ | ||
| patch.object(common, 'get_port_device', side_effect=lambda p: objs[p]): | ||
| assert common.get_cpo_obj_dict(port_mapping) == {} | ||
| assert set(common.get_pluggable_obj_dict(port_mapping)) == {0, 1, 2} | ||
|
|
||
| def test_accessors_return_empty_without_port_mapping(self): | ||
| with patch.object(common, 'get_port_device') as mock_get_port_device: | ||
| assert common.get_cpo_obj_dict(None) == {} | ||
| assert common.get_pluggable_obj_dict(None) == {} | ||
|
|
||
| port_mapping = MagicMock() | ||
| port_mapping.physical_to_logical = None | ||
| assert common.get_cpo_obj_dict(port_mapping) == {} | ||
| assert common.get_pluggable_obj_dict(port_mapping) == {} | ||
| mock_get_port_device.assert_not_called() | ||
|
|
||
| def test_accessors_skip_ports_raising_exceptions(self): | ||
| objs = self._make_obj_dict() | ||
|
|
||
| def mock_get_port_device(physical_port): | ||
| if physical_port == 2: | ||
| raise ValueError("Invalid port") | ||
| return objs[physical_port] | ||
|
|
||
| with patch.object(common, 'is_cpo_port', return_value=False), \ | ||
| patch.object(common, 'get_port_device', side_effect=mock_get_port_device): | ||
| pluggable = common.get_pluggable_obj_dict(self._make_port_mapping()) | ||
| assert set(pluggable.keys()) == {0, 1} |
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| #!/usr/bin/env python3 | ||
|
|
||
| try: | ||
| from ..cmis.cmis_manager_task import CmisManagerTask | ||
| except ImportError as e: | ||
| raise ImportError(str(e) + " - required module not found") | ||
|
|
||
|
|
||
| class CpoManagerTask(CmisManagerTask): | ||
| def __init__(self, namespaces, port_mapping, port_obj_dict, main_thread_stop_event, skip_cpo_mgr=False): | ||
| super().__init__(namespaces, port_mapping, port_obj_dict, main_thread_stop_event, | ||
| skip_cmis_mgr=skip_cpo_mgr) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bgallagher-nexthop I don't see any use case for skip . can we remove? Cpo manager is mandatory for CPO platforms. Also, the skip cmis_mgr is not applicable for CPO platforms. A cpo platform can have two independent task CpoManagerTask and CmisManagerTask but when you pass the skip_cmis_mgr inside CpoManagerTask, its confusing. Lets create the task based upon port dict has sfp or cpo objects
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was requested by @tshalvi here, for platforms that may manage the state machine via firmware: sonic-net/SONiC#2444 (comment) Do you require this functionality @tshalvi ? |
||
| self.name = "CpoManagerTask" | ||
|
bgallagher-nexthop marked this conversation as resolved.
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| #!/usr/bin/env python3 | ||
|
|
||
| try: | ||
| from ..xcvrd import SfpStateUpdateTask | ||
| except ImportError as e: | ||
| raise ImportError(str(e) + " - required module not found") | ||
|
|
||
|
|
||
| class CpoStateUpdateTask(SfpStateUpdateTask): | ||
| def __init__(self, namespaces, port_mapping, port_obj_dict, main_thread_stop_event, sfp_error_event): | ||
| super().__init__(namespaces, port_mapping, port_obj_dict, main_thread_stop_event, sfp_error_event) | ||
| self.name = "CpoStateUpdateTask" |
|
bgallagher-nexthop marked this conversation as resolved.
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| #!/usr/bin/env python3 | ||
|
|
||
| try: | ||
| from ..dom.dom_mgr import DomInfoUpdateTask | ||
| except ImportError as e: | ||
| raise ImportError(str(e) + " - required module not found") | ||
|
|
||
|
|
||
| class CpoDomInfoUpdateTask(DomInfoUpdateTask): | ||
| name = "CpoDomInfoUpdateTask" |
Uh oh!
There was an error while loading. Please reload this page.