Add 'alias mode' support for show commands#298
Conversation
show/main.py
Outdated
| def __init__(self): | ||
| self.path = os.getcwd() | ||
| self.aliases = {} | ||
| global port_dict |
There was a problem hiding this comment.
Prefer not to introduce new global variables if possible. Since you're initializing this in the Config class, why not make it a member of the class? Or better yet, create a new class for name-to-alias translation, and make this a member and interface_name_to_alias() a method of that class.
There was a problem hiding this comment.
Addressed in the new commit
show/main.py
Outdated
| for port_name in port_dict.keys(): | ||
| if interface_name == port_name: | ||
| return port_dict[port_name]['alias'] | ||
| print "Invalid interface {}".format(interface_name) |
There was a problem hiding this comment.
Use click.echo() instead of print for consistency.
There was a problem hiding this comment.
Addressed in the new commit
show/main.py
Outdated
| if interface_name != "": | ||
| output = output.replace(interface_name, | ||
| interface_name_to_alias(interface_name)) | ||
| print output.rstrip('\n') |
There was a problem hiding this comment.
Use click.echo() instead of print for consistency.
There was a problem hiding this comment.
Addressed in the new commit
show/main.py
Outdated
|
|
||
|
|
||
| def run_command_in_alias_mode(command, linux_command=False): | ||
| """Replace command output results from default |
There was a problem hiding this comment.
Clarify docstring. Suggest: "Run command and replace all instances of SONiC interface names in output with vendor-specific interface aliases."
There was a problem hiding this comment.
Addressed in the new commit
show/main.py
Outdated
| break | ||
| if output: | ||
| if linux_command: | ||
| result = re.findall('Ethernet\w+', output) |
There was a problem hiding this comment.
This assumes all SONiC interfaces names begin with "Ethernet". This is not necessarily true. It is also possible that vendor aliases begin with "Ethernet", so you will try to convert those, as well. This should instead try to find instances of all SONiC interface names configured for the current device.
There was a problem hiding this comment.
Addressed in the new commit
show/main.py
Outdated
| r.append(data[k]['vlanid']) | ||
| r.append(m) | ||
| if get_interface_mode() == "alias": | ||
| alias = interface_name_to_alias(m) |
There was a problem hiding this comment.
Delete extra space after =
There was a problem hiding this comment.
Addressed in the new commit
|
In this new pull request implemented portalias feature for sonic-specific commands mentioned below:
As discussed implemented an Alias class for most of the portalias related functions. Most the above commands are in tabulated form. In function run_command_in_alias_mode else part handles above commands. Above command reusults consist of 3 parts. Header, Underline, and tabulate data. These are splitted into different categories and handled accordingly. |
show/main.py
Outdated
| pass | ||
|
|
||
|
|
||
| class Alias(object): |
There was a problem hiding this comment.
Please give class a more descriptive name, something along the lines of "InterfaceAliasConverter"
There was a problem hiding this comment.
Addressed in the new commit...
show/main.py
Outdated
|
|
||
|
|
||
| class Alias(object): | ||
| """Object to Alias conversion""" |
There was a problem hiding this comment.
What is "Object"? Description should be more along the lines of "Class which handles conversion between interface name and alias"
There was a problem hiding this comment.
Addressed in the new commit
show/main.py
Outdated
| """Object to Alias conversion""" | ||
|
|
||
| def __init__(self): | ||
| self.vendor_name_length = 0 |
There was a problem hiding this comment.
Rename: vendor_name_max_length
There was a problem hiding this comment.
Addressed in the new commit
| if interface_name == port_name: | ||
| return self.port_dict[port_name]['alias'] | ||
| click.echo("Invalid interface {}".format(interface_name)) | ||
|
|
There was a problem hiding this comment.
Remove blank line 67. Consider adding blank line between 65 and 66.
| if interface_alias == self.port_dict[port_name]['alias']: | ||
| return port_name | ||
| click.echo("Invalid interface {}".format(interface_alias)) | ||
|
|
There was a problem hiding this comment.
Remove blank line 78. Consider adding blank line between 76 and 77.
show/main.py
Outdated
| output = output.lstrip() | ||
| index = 0 | ||
|
|
||
| if command_type == 1: |
There was a problem hiding this comment.
command_type values as simple integers are not clearly defined (what does "1" mean? etc.). This makes them confusing. Also, if we have one "command_type" for each command, this will become unwieldy. It would be better if you could key off the command string itself, and not rely on an integer which represents the command.
There was a problem hiding this comment.
This is good one. Initially I thougt of using enums, but due to lack of package I ruled out the option. As suggested I used the command strings directly in the function and included in the latest commit.
show/main.py
Outdated
|
|
||
|
|
||
| # Global Alias object | ||
| _alias = InterfaceAliasConverter() |
There was a problem hiding this comment.
Change variable name to be more descriptive and reflect new class name.
There was a problem hiding this comment.
Addressed in the new commit..
show/main.py
Outdated
| self.vendor_name_max_length = len( | ||
| self.port_dict[port_name]['alias']) | ||
|
|
||
| def interface_name_to_alias(self, interface_name): |
There was a problem hiding this comment.
Now that the class name has changed to "InterfaceAliasConverter", I think you can remove "interface" from the method name (e.g., name_to_alias(self, interface_name):)
show/main.py
Outdated
| click.echo("Invalid interface {}".format(interface_name)) | ||
| raise click.Abort() | ||
|
|
||
| def interface_alias_to_name(self, interface_alias): |
There was a problem hiding this comment.
Now that the class name has changed to "InterfaceAliasConverter", I think you can remove "interface" from the method name (e.g., alias_to_name(self, interface_alias):)
There was a problem hiding this comment.
Addressed in the new commit..
show/main.py
Outdated
| elif output.startswith("Port Rx"): | ||
| output = output.replace("Port Rx", "Port Rx".rjust( | ||
| _alias.vendor_name_max_length)) | ||
| elif (command == "sudo sfputil show eeprom") or( |
There was a problem hiding this comment.
Can you simplify this to elif command.startswith("sudo sfputil show"):?
There was a problem hiding this comment.
Addressed in the new commit..
show/main.py
Outdated
| if output == '' and process.poll() is not None: | ||
| break | ||
| if output: | ||
| if linux_command: |
There was a problem hiding this comment.
Is there a way to make Linux commands the default (i.e., "else") case here, such that if the command doesn't match any of the if/elif cases, it is treated as a Linux command? This would eliminate the need for the linux_command parameter.
There was a problem hiding this comment.
I like the idea. But, without the linux_command it triggers lot of confusion in replacing interfaces from default to alias. Currently we are using different substitution methods for linux commands and sonic utilities. Without this differentiation we are forced to club the logic. This is an overhead to take care all possibilities. Would you like to do this approach?
There was a problem hiding this comment.
Could you explain what you mean by "we are forced to club the logic"? My thought is that if the command doesn't match any of the specific if command == ... clauses, it would fall through into a "default" case, and this case would handle all Linux commands.
It would allow us to remove a lot of duplicate code such as this:
if get_interface_mode() == "alias":
run_command_in_alias_mode(cmd, linux_command=True)
else:
run_command(cmd, display_cmd=verbose)
And instead, be able to check the alias mode inside run_command(), and if alias mode is enabled, then run_command() would call run_command_in_alias_mode().
There was a problem hiding this comment.
Joe, the linux/sonic commands use 2 different logics in replacing the interface names. I tested the else style of approach what you suggested. I moved the sonic_utitilies replacement logic inside a generic function "print_output_in_alias_mode" and included in most of the "if" statements.
if command == "portstat":
"""Show interface counters"""
if output.startswith("IFACE"):
output = output.replace("IFACE", "IFACE".rjust(
_interface.vendor_name_max_length))
print_output_in_alias_mode(output, index)<----
elif command == "pfcstat":
"""Show pfc counters"""
if output.startswith("Port Tx"):
output = output.replace("Port Tx", "Port Tx".rjust(
_interface.vendor_name_max_length))
elif output.startswith("Port Rx"):
output = output.replace("Port Rx", "Port Rx".rjust(
_interface.vendor_name_max_length))
print_output_in_alias_mode(output, index)<----
Since, sonic_utitilies replacement logic is safely binded inside the if statements now it is free to introduce else for linux_utitlites commands. This approach works fine.
I agree with checking alias mode inside the run_command. Will raise pull request post testing.
| if display_cmd: | ||
| click.echo(click.style("Command: ", fg='cyan') + click.style(command, fg='green')) | ||
|
|
||
| if get_interface_mode() == "alias" and not command.startswith("intfutil"): |
There was a problem hiding this comment.
Suggest to add a comment explaining that we don't translate the output of intfutil because it already displays both the name and alias for each interface.
There was a problem hiding this comment.
Addressed as above
show/main.py
Outdated
| word[index] = underline | ||
| output = ' ' .join(word) | ||
|
|
||
| # Replace default interface name to vendor-name |
There was a problem hiding this comment.
# Replace SONiC interface name with vendor alias
show/main.py
Outdated
| """Class which handles conversion between interface name and alias""" | ||
|
|
||
| def __init__(self): | ||
| self.vendor_name_max_length = 0 |
There was a problem hiding this comment.
Please rename to "alias_max_length"
There was a problem hiding this comment.
Addressed in new commit!
show/main.py
Outdated
| self.port_dict[port_name]['alias']) | ||
|
|
||
| def name_to_alias(self, interface_name): | ||
| """Return alias interface name if default |
There was a problem hiding this comment.
Clarify comment: "Return vendor interface alias if SONiC interface name is given as argument"
show/main.py
Outdated
| raise click.Abort() | ||
|
|
||
| def alias_to_name(self, interface_alias): | ||
| """Return default interface name if alias name is given as argument |
There was a problem hiding this comment.
Clarify comment: "Return SONiC interface name if vendor port alias given as argument"
show/main.py
Outdated
| body = [] | ||
|
|
||
| if interfacename is not None: | ||
|
|
|
Is there a reason you didn't add alias-to-name conversion for the "show interfaces transceiver ..." commands? If not, please add it. Also, please fix the merge conflict (it's only whitespace-related). Could you also please add a new command, |
|
transciever support is not added due to the lack of xvcrd implemetation in Dell boxes. Since it is added recently, I can able to test with the new change. Issue addresed in new commit. Addressed space conflicts and added "show interface naming_mode" command support in new commit.. |
show/main.py
Outdated
| iface_alias_converter.alias_max_length)) | ||
| print_output_in_alias_mode(output, index) | ||
|
|
||
| elif command == "sudo sfputil show eeprom": |
There was a problem hiding this comment.
This should be command.startswith("sudo sfputil show eeprom") because the command can take flags and an optional interface argument.
You also need to add code to translate the optional interface name from vendor alias to SONiC name in the "show interfaces transceiver ..." commands on lines 506-548 like you did in the other commands (see lines 560-562 for reference).
There was a problem hiding this comment.
Addressed and updated in new commit.. 👍
|
This will break 'show interface status' in vs, please check. |
|
@qiluo-msft: The root cause was due to the lack of a "PORT" table in ConfigDB on the virtual switch. The "PORT" table will be populated by this PR: sonic-net/sonic-buildimage#2169 |
|
@paavaanan @jleveque Your commit is breaking 'show interface status', 'show platform summary' and other cmds. Alias is an optional field. See #403 |
- What I did
Added portalias support for following commands. To enable alias output in CLI execute "sudo config interface_naming_mode alias" command and use the below commands.
- How I did it
Implemented a seperate run_command_in_alias_mode function to change the results of linux commands from default interface name to vendor specific name. This function read each line of the output and replace the equivalent vendor names. To speed up the result for mutli-line output ( show ip route/ipv6 route/arp) we cache the alias_names from port_config.ini at init. This results in quick substitution.
- How to verify it
Execute the commands as described above