-
Notifications
You must be signed in to change notification settings - Fork 664
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
Add 'alias mode' support for show commands #298
Add 'alias mode' support for show commands #298
Conversation
show/main.py
Outdated
@@ -28,6 +29,12 @@ class Config(object): | |||
def __init__(self): | |||
self.path = os.getcwd() | |||
self.aliases = {} | |||
global port_dict |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use click.echo()
instead of print
for consistency.
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use click.echo()
instead of print
for consistency.
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in the new commit
show/main.py
Outdated
@@ -955,7 +1034,11 @@ def tablelize(keys, data): | |||
r = [] | |||
r.append(k) | |||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Delete extra space after =
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.
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
@@ -38,6 +40,45 @@ def read_config(self, filename): | |||
pass | |||
|
|||
|
|||
class Alias(object): |
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.
Please give class a more descriptive name, something along the lines of "InterfaceAliasConverter"
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.
Addressed in the new commit...
show/main.py
Outdated
@@ -38,6 +40,45 @@ def read_config(self, filename): | |||
pass | |||
|
|||
|
|||
class Alias(object): | |||
"""Object to Alias conversion""" |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename: vendor_name_max_length
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.
Addressed in the new commit
show/main.py
Outdated
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove blank line 67. Consider adding blank line between 65 and 66.
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.
👍
show/main.py
Outdated
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove blank line 78. Consider adding blank line between 76 and 77.
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.
👍
show/main.py
Outdated
output = output.lstrip() | ||
index = 0 | ||
|
||
if command_type == 1: |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change variable name to be more descriptive and reflect new class name.
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you simplify this to elif command.startswith("sudo sfputil show"):
?
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
show/main.py
Outdated
@@ -116,6 +156,10 @@ def run_command(command, display_cmd=False): | |||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please rename to "alias_max_length"
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clarify comment: "Return vendor interface alias if SONiC interface name is given as argument"
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.
👍
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clarify comment: "Return SONiC interface name if vendor port alias given as argument"
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.
👍
show/main.py
Outdated
@@ -207,6 +402,10 @@ def alias(interfacename): | |||
body = [] | |||
|
|||
if interfacename is not None: | |||
|
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.
Remove blank line
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.
👍
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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