-
Notifications
You must be signed in to change notification settings - Fork 1k
Generate PFC storm using PFC backpressure on additional Arista devices #17971
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
Closed
veronica-arista
wants to merge
7
commits into
sonic-net:master
from
veronica-arista:pfc_gen_backpressure_th_th2
Closed
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
71fd531
Generate PFC storm using PFC backpressure on TH5
veronica-arista 44459e1
Update the pfc_gen with backpressure for Arista device to work on Sonic
veronica-arista 8364bf8
Merge remote-tracking branch 'upstream/master' into master-pfcwd-stor…
veronica-arista bce1f84
Fix indentation
veronica-arista 14eaaef
Resolve pre-commit check errors
veronica-arista bfbe274
Merge remote-tracking branch 'upstream/master' into master-pfcwd-stor…
veronica-arista b08f825
Add support for TH and TH2 chips for pfc_gen using backpresure
veronica-arista File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,239 @@ | ||
| #!/usr/bin/env python | ||
|
|
||
| """ | ||
| Script to generate PFC storm. | ||
|
|
||
| """ | ||
| import binascii | ||
| import sys | ||
| import optparse | ||
| import logging | ||
| import logging.handlers | ||
| import re | ||
| import signal | ||
| import subprocess | ||
| import time | ||
|
|
||
|
|
||
| logger = logging.getLogger('MyLogger') | ||
| logger.setLevel(logging.DEBUG) | ||
|
|
||
|
|
||
| class SignalCleanup(): | ||
| def __init__(self, fanoutPfcStorm, endMsg): | ||
| self.fanoutPfcStorm = fanoutPfcStorm | ||
| self.endMsg = endMsg | ||
| signal.signal(signal.SIGTERM, self.sigHandler) | ||
|
|
||
| def sigHandler(self, *args): | ||
| self.fanoutPfcStorm.endAllPfcStorm() | ||
|
|
||
| logger.debug(self.endMsg) | ||
| sys.exit(0) | ||
|
|
||
|
|
||
| class FanoutPfcStorm(): | ||
| ''' | ||
| For eos this class expects all interfaces to be in the front panel interface format | ||
| ex. Ethernet1/1 and not et1_1 | ||
| For sonic, the interfaces are the default format | ||
| ''' | ||
| def __init__(self, priority, chipName, os): | ||
| self.intfsEnabled = [] | ||
| self.priority = priority | ||
| self.switchChip = chipName | ||
| self.os = os | ||
| self.intfToMmuPort, self.intfToPort = self._parseInterfaceMapFullSonic() if os == 'sonic' else self._parseInterfaceMapFull() | ||
|
|
||
| def _shellCmd(self, cmd): | ||
| output = "" | ||
| result = subprocess.run([f"{cmd}"], capture_output=True, text=True, shell=True) | ||
| if result.returncode == 0: | ||
| output = result.stdout | ||
| return output | ||
|
|
||
| def _cliCmd(self, cmd): | ||
| output = "" | ||
| if self.os == 'sonic': | ||
| result = subprocess.run([f"bcmcmd '{cmd}'"], capture_output=True, text=True, shell=True) | ||
| else: | ||
| result = subprocess.run( | ||
| ["Cli", "-c", f"{cmd}"], capture_output=True, text=True) | ||
| if result.returncode == 0: | ||
| output = result.stdout | ||
| return output | ||
|
|
||
| def _bcmltshellCmd(self, cmd): | ||
| if self.os == 'sonic': | ||
| return self._cliCmd(f"bsh -c \"{cmd}\"") | ||
| else: | ||
| return self._cliCmd(f"en\nplatform trident shell\nbcmltshell\n{cmd}") | ||
|
|
||
| def _bcmshellCmd(self, cmd): | ||
| return self._cliCmd(f"en\nplatform trident shell\n{cmd}") | ||
|
|
||
| def _parseInterfaceMapFull(self): | ||
| intfToMmuPort = {} | ||
| intfToPort = {} | ||
|
|
||
| output = self._cliCmd(f"en\nshow platform trident interface map full") | ||
|
|
||
| for line in output.splitlines(): | ||
| mo = re.search('Intf: (?P<intf>Ethernet\S+).{1,50}Port: {1,3}(?P<port>\S+).{1,100}P2M\[ {0,3}\d+\]: {1,3}(?P<mmu>\S+)', line) | ||
| if mo is None: | ||
| continue | ||
| intfToMmuPort[mo.group('intf')] = mo.group('mmu') | ||
| intfToPort[mo.group('intf')] = mo.group('port') | ||
|
|
||
| return intfToMmuPort, intfToPort | ||
|
|
||
| def _parseInterfaceMapFullSonic(self): | ||
| intfToMmuPort = {} | ||
| intfTolPort = {} | ||
| lPortToIntf = {} | ||
|
|
||
| if self.switchChip.startswith(("Tomahawk5", "Tomahawk4")): | ||
| output = self._bcmltshellCmd('knet netif info') | ||
| for info in output.split("Network interface Info:"): | ||
| mo = re.search(r"Name: (?P<intf>Ethernet\d+)[\s\S]{1,100}Port: (?P<lport>\d+)", info) | ||
| if mo is None: | ||
| continue | ||
| lPortToIntf[mo.group('lport')] = mo.group('intf') | ||
| intfTolPort[mo.group('intf')] = mo.group('lport') | ||
| output = self._cliCmd("show portmap") | ||
| for line in output.splitlines(): | ||
| entries = line.split() | ||
| if len(entries) == 7: | ||
| lport = entries[2] | ||
| mmuPort = entries[4] | ||
| if lport in lPortToIntf: | ||
| intfToMmuPort[lPortToIntf[lport]] = mmuPort | ||
| intfTolPort[mo.group('intf')] = mo.group('lport') | ||
| else: | ||
| output = self._cliCmd('knet netif show') | ||
| for info in output.split("Interface ID"): | ||
| mo = re.search(r"name=(?P<intf>Ethernet\d+)[\s\S]{1,100}port=(?P<lport>\S+)", info) | ||
| if mo is None: | ||
| continue | ||
| lPortToIntf[mo.group('lport')] = mo.group('intf') | ||
| intfTolPort[mo.group('intf')] = mo.group('lport') | ||
| output = self._cliCmd("show portmap") | ||
| for line in output.splitlines(): | ||
| entries = line.split() | ||
| if len(entries) == 9: | ||
| lport = entries[0] | ||
| mmuPort = entries[4] | ||
| if lport in lPortToIntf: | ||
| intfToMmuPort[lPortToIntf[lport]] = mmuPort | ||
| intfTolPort[mo.group('intf')] = mo.group('lport') | ||
|
|
||
| return intfToMmuPort, intfTolPort | ||
|
|
||
| def _endPfcStorm(self, intf): | ||
| ''' | ||
| Intf format is Ethernet1/1 | ||
|
|
||
| The users of this class are only expected to call | ||
| startPfcStorm and endAllPfcStorm | ||
| ''' | ||
| mmuPort = self.intfToMmuPort[intf] | ||
| port = self.intfToPort[intf] | ||
| if self.switchChip.startswith(("Tomahawk5", "Tomahawk4")): | ||
| self._bcmltshellCmd(f"pt MMU_INTFO_XPORT_BKP_HW_UPDATE_DISr set BCMLT_PT_PORT={mmuPort} PAUSE_PFC_BKP=0") | ||
| self._bcmltshellCmd(f"pt MMU_INTFO_TO_XPORT_BKPr set BCMLT_PT_PORT={mmuPort} PAUSE_PFC_BKP=0") | ||
| else: | ||
| self._bcmshellCmd(f"setreg CHFC2PFC_STATE.{port} PRI_BKP=0") | ||
| self._cliCmd(f"en\nconf\n\nint {intf}\nno priority-flow-control on") | ||
| if self.os == 'sonic': | ||
| for prio in range(8): | ||
| self._cliCmd(f"config interface pfc priority {intf} {prio} off") | ||
| else: | ||
| for prio in range(8): | ||
| self._cliCmd(f"en\nconf\n\nint {intf}\nno priority-flow-control priority {prio} no-drop") | ||
|
|
||
| def startPfcStorm(self, intf): | ||
| if intf in self.intfsEnabled: | ||
| return | ||
| self.intfsEnabled.append(intf) | ||
|
|
||
| mmuPort = self.intfToMmuPort[intf] | ||
| port = self.intfToPort[intf] | ||
| if self.os == 'sonic': | ||
| for prio in range(8): | ||
| if (1 << prio) & self.priority: | ||
| self._shellCmd(f"config interface pfc priority {intf} {prio} on") | ||
| else: | ||
| self._cliCmd(f"en\nconf\n\nint {intf}\npriority-flow-control on") | ||
| for prio in range(8): | ||
| if (1 << prio) & self.priority: | ||
| self._cliCmd(f"en\nconf\n\nint {intf}\npriority-flow-control priority {prio} no-drop") | ||
|
|
||
| if self.switchChip.startswith(("Tomahawk5", "Tomahawk4")): | ||
| self._bcmltshellCmd(f"pt MMU_INTFO_XPORT_BKP_HW_UPDATE_DISr set BCMLT_PT_PORT={mmuPort} PAUSE_PFC_BKP=1") | ||
| self._bcmltshellCmd(f"pt MMU_INTFO_TO_XPORT_BKPr set BCMLT_PT_PORT={mmuPort} PAUSE_PFC_BKP={self.priority}") | ||
| else: | ||
| self._bcmshellCmd(f"setreg CHFC2PFC_STATE.{port} PRI_BKP={self.priority}") | ||
|
|
||
| def endAllPfcStorm(self): | ||
| for intf in self.intfsEnabled: | ||
| self._endPfcStorm(intf) | ||
|
|
||
|
|
||
| def main(): | ||
| usage = "usage: %prog [options] arg1 arg2" | ||
| parser = optparse.OptionParser(usage=usage) | ||
| parser.add_option("-i", "--interface", type="string", dest="interface", | ||
| help="Interface list to send packets, separated by ','", metavar="Interface") | ||
| parser.add_option('-p', "--priority", type="int", dest="priority", | ||
| help="PFC class enable bitmap.", metavar="Priority", default=-1) | ||
| parser.add_option("-r", "--rsyslog-server", type="string", dest="rsyslog_server", | ||
| default="127.0.0.1", help="Rsyslog server IPv4 address", metavar="IPAddress") | ||
| parser.add_option("-c", "--chipName", type="string", dest="chipName", metavar="ChipName", | ||
| help="Name of chip in the switch, i.e. Tomahawk5") | ||
| parser.add_option("-o", "--os", type="string", dest="os", | ||
| help="Operating system (eos or sonic)", default="eos") | ||
|
|
||
| (options, args) = parser.parse_args() | ||
|
|
||
| if options.interface is None: | ||
| print("Need to specify the interface to send PFC pause frame packets.") | ||
| parser.print_help() | ||
| sys.exit(1) | ||
|
|
||
| if options.chipName is None: | ||
| print("Need to specify the ChipName to determine what cli to generate PFC pause frame packets.") | ||
| parser.print_help() | ||
| sys.exit(1) | ||
|
|
||
| if options.priority > 255 or options.priority < 0: | ||
| print("Enable class bitmap is not valid. Need to be in range 0-255.") | ||
| parser.print_help() | ||
| sys.exit(1) | ||
|
|
||
| # Configure logging | ||
| handler = logging.handlers.SysLogHandler(address=(options.rsyslog_server, 514)) | ||
| logger.addHandler(handler) | ||
|
|
||
| # List of front panel kernel intfs | ||
| interfaces = options.interface.split(',') | ||
|
|
||
| fs = FanoutPfcStorm(options.priority, options.chipName, options.os) | ||
| fsCleanup = SignalCleanup(fs, 'PFC_STORM_END') | ||
|
|
||
| logger.debug('PFC_STORM_DEBUG') | ||
| for intf in interfaces: | ||
| if options.os == 'eos': | ||
| intf = frontPanelIntfFromKernelIntfName(intf) | ||
| fs.startPfcStorm(intf) | ||
| logger.debug('PFC_STORM_START') | ||
|
|
||
| # wait forever until stop | ||
| while True: | ||
| time.sleep(100) | ||
|
|
||
| def frontPanelIntfFromKernelIntfName(intf): | ||
| return intf.replace("et", "Ethernet").replace("_", "/") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| bash | ||
| cd {{pfc_gen_dir}} | ||
| {% if (pfc_asym is defined) and (pfc_asym == True) %} | ||
| {% if pfc_storm_defer_time is defined %}sleep {{pfc_storm_defer_time}} &&{% endif %} sudo python3 {{pfc_gen_file}} -c {{pfc_gen_chip_name}} -p {{pfc_queue_index}} -i {{pfc_fanout_interface | replace("Ethernet", "et") | replace("/", "_")}} > /dev/null 2>&1 & | ||
| {% else %} | ||
| {% if pfc_storm_defer_time is defined %}sleep {{pfc_storm_defer_time}} &&{% endif %} sudo python3 {{pfc_gen_file}} -c {{pfc_gen_chip_name}} -p {{(1).__lshift__(pfc_queue_index)}} -i {{pfc_fanout_interface | replace("Ethernet", "et") | replace("/", "_")}} -r {{ansible_eth0_ipv4_addr}} > /dev/null 2>&1 & | ||
| {% endif %} | ||
| exit | ||
| exit | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| cd {{pfc_gen_dir}} | ||
| {% if (pfc_asym is defined) and (pfc_asym == True) %} | ||
| nohup sh -c "{% if pfc_storm_defer_time is defined %}sleep {{pfc_storm_defer_time}} &&{% endif %} sudo python3 {{pfc_gen_file}} -c {{pfc_gen_chip_name}} -p {{pfc_queue_index}} -o sonic -i {{pfc_fanout_interface}}" > /dev/null 2>&1 & | ||
| {% else %} | ||
| nohup sh -c "{% if pfc_storm_defer_time is defined %}sleep {{pfc_storm_defer_time}} &&{% endif %} sudo python3 {{pfc_gen_file}} -c {{pfc_gen_chip_name}} -p {{(1).__lshift__(pfc_queue_index)}} -o sonic -i {{pfc_fanout_interface}} -r {{ansible_eth0_ipv4_addr}}" > /dev/null 2>&1 & | ||
| {% endif %} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| bash | ||
| cd {{pfc_gen_dir}} | ||
| {% if (pfc_asym is defined) and (pfc_asym == True) %} | ||
| {% if pfc_storm_stop_defer_time is defined %}sleep {{pfc_storm_stop_defer_time}} &&{% endif %} sudo pkill -f 'python3 {{pfc_gen_file}} -c {{pfc_gen_chip_name}} -p {{pfc_queue_index}} -i {{pfc_fanout_interface | replace("Ethernet", "et") | replace("/", "_")}}' > /dev/null 2>&1 & | ||
| {% else %} | ||
| {% if pfc_storm_stop_defer_time is defined %}sleep {{pfc_storm_stop_defer_time}} &&{% endif %} sudo pkill -f 'python3 {{pfc_gen_file}} -c {{pfc_gen_chip_name}} -p {{(1).__lshift__(pfc_queue_index)}} -i {{pfc_fanout_interface | replace("Ethernet", "et") | replace("/", "_")}} -r {{ansible_eth0_ipv4_addr}}' > /dev/null 2>&1 & | ||
| {% endif %} | ||
| exit | ||
| exit |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| cd {{pfc_gen_dir}} | ||
| {% if (pfc_asym is defined) and (pfc_asym == True) %} | ||
| nohup sh -c "{% if pfc_storm_stop_defer_time is defined %}sleep {{pfc_storm_stop_defer_time}} &&{% endif %} sudo pkill -f 'python {{pfc_gen_file}} -c {{pfc_gen_chip_name}} -p {{pfc_queue_index}} -o sonic -i {{pfc_fanout_interface}}'" > /dev/null 2>&1 & | ||
| {% else %} | ||
| nohup sh -c "{% if pfc_storm_stop_defer_time is defined %}sleep {{pfc_storm_stop_defer_time}} &&{% endif %} sudo pkill -f 'python {{pfc_gen_file}} -c {{pfc_gen_chip_name}} -p {{(1).__lshift__(pfc_queue_index)}} -o sonic -i {{pfc_fanout_interface}} -r {{ansible_eth0_ipv4_addr}}'" > /dev/null 2>&1 & | ||
| {% endif %} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this new feature only support python3? Are there any limitations, thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no testing on python2. Python2 is long out of support.