Skip to content

[config] Add support for accepting port alias in lieu of port name#260

Merged
jleveque merged 10 commits intosonic-net:masterfrom
paavaanan:portalias-for-interfaces
Aug 14, 2018
Merged

[config] Add support for accepting port alias in lieu of port name#260
jleveque merged 10 commits intosonic-net:masterfrom
paavaanan:portalias-for-interfaces

Conversation

@paavaanan
Copy link
Contributor

@paavaanan paavaanan commented May 30, 2018

portalias-logs.txt

- What I did
Implemented portalias feature for two basic commands "config interface shutdown/startup"

-Introduced new CLI to configure the interface mode:
-config interface_mode "alias"
-config interface_mode "defaut"

User can stick to any mode. Mode selection is unique for each sessions. Default mode stick to sonic ethernet interface style and Alias mode stick to individual switch vendor interface style. User selection sticks to the current logged session. If multiple login session do exists it maintains the user selection seperately.

- How I did it

  1. Introduced convert_interface_name() function to toggle interfaces between alias/default mode.
  2. Included a termporary file for each individual session to maintain the mode selection.
  3. interface_mode.sh script is included in /etc/profile.d to create a unique file for each session at the time of login.

- How to verify it

  1. config interface_mode alias
  2. config interface shutdown fortyGigE1/1/7
  3. config interface_mode default
  4. config interface startup Ethernet6

--> interface_mode.sh is raised in sonic-net/sonic-buildimage#1751

@jleveque jleveque changed the title add support for portalias feature [config] Add support for accepting port alias in lieu of port name Jun 5, 2018
config/main.py Outdated

def set_interface_mode(mode):
tmp = "/tmp/"
output = subprocess.check_output(['tty']).strip('\n')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After our discussion earlier, I understand that the interface_status.sh script in PR sonic-net/sonic-buildimage#1751 will remove stale entries upon a new tty connection. However, I am still concerned that this approach is unnecessarily complicated.

Why not simply set an environment variable per user (e.g., export SONIC_CLI_ALIAS_MODE="true")? Using this approach, we could also cause the setting to persist between sessions by writing this to the user's .profile file. If the variable is either "false" or not defined, we assume input will be SONiC port names (the default behavior). If the variable is "true" we assume the input will be port aliases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Initially we approched the issue in the above context. Since python is the child process of parent bash (as like for any sessios) we can inherit the status of enviroment variable say SONIC_CLI_ALIAS_MODE to python but we can't revert back the state to TRUE/FALSE back to bash environment variable. . The change do exists until the script terminates. SONIC_CLI_ALIAS_MODE value cannot be exported to parent bash.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes ... you can't modify the environment of the parent shell. That's a problem. If we wanted to proceed with this approach, I guess we would need to prompt the user to log out and log back in again for the change to take effect; I think that's quite an inconvenience.

@lguohan: Do you have any alternative suggestions? Otherwise, I believe the tty-based approach may be our best bet.

config/main.py Outdated
if proc.returncode != 0 and not ignore_error:
sys.exit(proc.returncode)

def convert_interface_name(interface_name):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function will blindly convert a port name to a port alias and vice-versa. This could pose issues if a device shares the same string for both a port name and a port alias, as it will always find a match on line 49 and return that port's alias.

I think it is better to have two functions with distinct functionality: interface_name_to_alias(interface_name) and interface_alias_to_name(interface_alias).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Introduced interface_alias_to_name() to convert the alias to default mode. In the current code change, there is no any need for interface_name_to_alias() conversion. While implementing the feature for other subset of commands if needed will implement on need basis.

config/main.py Outdated
if proc.returncode != 0 and not ignore_error:
sys.exit(proc.returncode)

def interface_alias_to_name(interface_name):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter should be called interface_alias. Otherwise it's very confusing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay

@paavaanan
Copy link
Contributor Author

• Modified the design to user based approach. Harnessed the power of /etc/sudoers and /etc/environment file to retain the environment variable value even in sudo sessions. So, when the user request the change from default mode to alias mode, the action is completed in two phases.
• config/main.py script fetch the current value of IFMODE and modify as user requested alias mode and write in bashrc.
• When the user logout and login the session bashrc update the environment variable.
• This works for both root, and non-root sessions.

@paavaanan
Copy link
Contributor Author

  • In this latest commit, addressed review comments.
  • Maintained uniformity in naming convention.
  • Environement variable IFMODE alias/default switch code is refined.

port_alias_config.log

config/main.py Outdated
f = open(bashrc,'w')
f.write(newdata)
f.close()
print "Please logout and login again!"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest change to "Please log out and log back in for changes take effect."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed

config/main.py Outdated
if interface_name == port_dict[port_name]['alias']:
return port_name
print "Invalid interface {}".format(interface_name)
sys.exit(0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this is a utility function, I'm not sure it should cause the program to exit. Maybe return None and have the caller exit if it does?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment addressed below

config/main.py Outdated
if interface_name == port_name:
return port_dict[port_name]['alias']
print "Invalid interface {}".format(interface_name)
sys.exit(0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this is a utility function, I'm not sure it should cause the program to exit. Maybe return None and have the caller exit if it does?

Copy link
Contributor Author

@paavaanan paavaanan Jul 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shutdown command as an example:

def shutdown(interface_name, verbose):
    """Shut down interface"""
    print "lias"
    if get_interface_mode() == "alias":
        interface_name = interface_alias(interface_name)
  • If we fail to return sys.exit(0) in function interface_alias() it propogates to next line and execute "ip link set <interface_nam> down. Where interface_name is invalid.
  • This is unnecessary and tends to double opertaion. To avoid this sys.exit(0) introduced. and also it alerts the user about invalid interface.
  • If you are skeptical to the exit in utiltiy function we can shift the exit in parent shutdown().
    command = "ip link set {} down".format(interface_name)
    run_command(command, display_cmd=verbose)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I think the caller functions should check for None, print an error and exit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will addres the changes

config/main.py Outdated
if proc.returncode != 0 and not ignore_error:
sys.exit(proc.returncode)

def interface_alias(interface_name):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was not the change I requested. I requested you change the parameter name to interface_alias, not the function name. It should be def interface_alias_to_name(interface_alias):

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clarification


port_dict = json.loads(p.stdout.read())

if interface_name is not None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens in the case where interface_name is None?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • The interface_name validation is done by pthon click package at the initial phase of the function it throws missing argument error.
admin@sonic:~$ sudo config interface shutdown None
Invalid interface None
admin@sonic:~$ sudo config interface shutdown
Usage: config interface shutdown [OPTIONS] <interface_name>
Error: Missing argument "interface_name".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't asking what happens if a user provides the string "None" as the interface name.

My question is regarding the fact that you have an if statement (if interface_name is not None:) which returns a value but no corresponding else statement. Thus, if interface_name is of type None (not the string "None"), this function will not return anything. I think this was overlooked, so I was asking for an explanation to see if this is the behavior you expect.

Copy link
Contributor

@jleveque jleveque Jul 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't asking what happens if a user provides the string "None" as the interface name.

My question is regarding the fact that you have an if statement (if interface_name is not None:) which returns a value, but there is no corresponding else statement. Thus, if interface_name is of type None (not the string "None"), this function will not return anything. I think this was overlooked, so I was asking for an explanation to see if this is the behavior you expect.

Copy link
Contributor Author

@paavaanan paavaanan Jul 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Joe I got your point. When the interface_name variable type is None, even though else is not defined inside the corresponding function the return type of interface_alias_to_name() will be None. All parent functions are equipped to handle this case. So, Invalid Interface message will be raised to the user. Even if we add an else the behavior will be the same.

    """Shut down interface"""
    if get_interface_mode() == "alias":
        interface_name = interface_alias_to_name(interface_name)
        if interface_name is None:
            print "Invalid interface {}".format(interface_name)<--------
            raise click.Abort()
)
admin@sonic:~$ sudo config interface shutdown hundredGigE1/1/120
Invalid interface hundredGigE1/1/120
Aborted!
admin@sonic:~$

Copy link
Contributor

@jleveque jleveque Aug 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still believe that every path should explicitly return a value. Otherwise the behavior of the function isn't easily determined by the reader. I think you can easily fix this as follows:

    if interface_alias is not None:
        for port_name in natsorted(port_dict.keys()):
            if interface_alias == port_dict[port_name]['alias']:
                return port_name
        print "Invalid interface {}".format(interface_alias)
       
    return None

config/main.py Outdated
print "Please logout and login again!"

def get_interface_mode():
mode = os.getenv('IFMODE')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the environment variable is not set? You should probably return "default" in this case so that this function consistently returns a value.

Copy link
Contributor Author

@paavaanan paavaanan Jul 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will address this.

config/main.py Outdated

@cli.group()
def interface_mode():
"""Interface mode change tasks"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest """Modify interface naming mode for interacting with SONiC CLI"""

config/main.py Outdated

@interface_mode.command('default')
def interface_mode_default():
"""Set interface mode to DEFAULT"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest """Set CLI interface naming mode to DEFAULT (SONiC port name)"""

config/main.py Outdated

@interface_mode.command('alias')
def interface_mode_alias():
"""Set interface mode to ALIAS"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest """Set CLI interface naming mode to ALIAS (Vendor port alias)"""

config/main.py Outdated
#

@cli.group()
def interface_mode():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest change to interface_naming_mode()

if not sudo_user:
bashrc = "/{}/.bashrc".format(user)
user = os.getenv('SUDO_USER')
bashrc_ifacemode_line = "SONIC_CLI_IFACE_MODE={}".format(mode)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this line is best moved down just above line 87, right before it is used. That will also keep all of the user name determination code together.

config/main.py Outdated
newdata = re.sub(r"IFMODE=\w+",set_mode,filedata)

newdata = re.sub(r"SONIC_CLI_IFACE_MODE=\w+", bashrc_ifacemode_line, filedata)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete whitespace.

config/main.py Outdated

if get_interface_mode() == "alias":
interface_name = interface_alias(interface_name)
default_name = interface_alias_to_name(interface_name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The introduction of the variable default_name is unnecessary. Simply assign directly to interface_name and compare it to None. You can then also eliminate the else clause:

interface_name = interface_alias_to_name(interface_name)
if interface_name is None:
    print "Invalid interface {}".format(interface_name)
    raise click.Abort()

config/main.py Outdated
interface_name = interface_alias(interface_name)
default_name = interface_alias_to_name(interface_name)
if default_name is None:
print "Invalid interface {}".format(interface_name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interface_name is not defined. This will crash. However, instead of replacing it with default_name, please refactor the code as I suggested in my comment on line 411.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do this crash? interface_name is the incoming argument for add_vlan_member() So, argument do have a value when invoked.

def add_vlan_member(ctx, vid, interface_name, untagged):

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're correct. Technically, Click should prevent interface_name from being None. I think I got a bit lost due the introduction of default_interface. Ignore this comment. #Closed

config/main.py Outdated
alias_name = interface_name_to_alias(interface_name)
if alias_name is None:
print "Invalid interface {}".format(interface_name)
sys.exit(0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please replace sys.exit(0) raise click.Abort(), which will cause the program to exit with code 1.

config/main.py Outdated

if get_interface_mode() == "alias":
interface_name = interface_alias(interface_name)
default_name = interface_alias_to_name(interface_name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please refactor this code in the same manner as I suggested in my comment on line 411.

config/main.py Outdated
"""Shut down interface"""
if get_interface_mode() == "alias":
interface_name = interface_alias(interface_name)
default_name = interface_alias_to_name(interface_name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please refactor this code in the same manner as I suggested in my comment on line 411.

config/main.py Outdated
"""Start up interface"""
if get_interface_mode() == "alias":
interface_name = interface_alias(interface_name)
default_name = interface_alias_to_name(interface_name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please refactor this code in the same manner as I suggested in my comment on line 411.

config/main.py Outdated
"""Set interface speed"""
if get_interface_mode() == "alias":
interface_name = interface_alias(interface_name)
default_name = interface_alias_to_name(interface_name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please refactor this code in the same manner as I suggested in my comment on line 411.

@@ -0,0 +1,75 @@
#Portalias
Copy link
Contributor

@jleveque jleveque Jul 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A design doc of this sort doesn't belong in this repo alongside the code; if anything it should be placed in the GitHub wiki. Please delete this file.

@paavaanan
Copy link
Contributor Author

@jleveque addressed all review comments and raised 2 commits with all changes.
Please, have a look on this.

config/main.py Outdated
bashrc = "/home/{}/.bashrc".format(user)
else:
sys.exit(0)
raise click.Abort
Copy link
Contributor

@jleveque jleveque Aug 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add parentheses for consistency: raise click.Abort()

config/main.py Outdated
interface_name = interface_alias_to_name(interface_name)
if interface_name is None:
sys.exit(0)
raise click.Abort
Copy link
Contributor

@jleveque jleveque Aug 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add parentheses for consistency: raise click.Abort()

config/main.py Outdated
interface_name = interface_alias_to_name(interface_name)
if interface_name is None:
sys.exit(0)
raise click.Abort
Copy link
Contributor

@jleveque jleveque Aug 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add parentheses for consistency: raise click.Abort()

config/main.py Outdated
interface_name = interface_alias_to_name(interface_name)
if interface_name is None:
sys.exit(0)
raise click.Abort
Copy link
Contributor

@jleveque jleveque Aug 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add parentheses for consistency: raise click.Abort()

config/main.py Outdated
interface_name = interface_alias_to_name(interface_name)
if interface_name is None:
sys.exit(0)
raise click.Abort
Copy link
Contributor

@jleveque jleveque Aug 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add parentheses for consistency: raise click.Abort()

config/main.py Outdated
interface_name = interface_alias_to_name(interface_name)
if interface_name is None:
sys.exit(0)
raise click.Abort
Copy link
Contributor

@jleveque jleveque Aug 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add parentheses for consistency: raise click.Abort()


port_dict = json.loads(p.stdout.read())

if interface_name is not None:
Copy link
Contributor

@jleveque jleveque Aug 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still believe that every path should explicitly return a value. Otherwise the behavior of the function isn't easily determined by the reader. I think you can easily fix this as follows:

    if interface_alias is not None:
        for port_name in natsorted(port_dict.keys()):
            if interface_alias == port_dict[port_name]['alias']:
                return port_name
        print "Invalid interface {}".format(interface_alias)
       
    return None


if "SONIC_CLI_IFACE_MODE" not in filedata:
newdata = filedata + bashrc_ifacemode_line
newdata += "\n"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest adding the newline directly to bashrc_ifacemode_line on line 78 as follows:

bashrc_ifacemode_line = "SONIC_CLI_IFACE_MODE={}\n".format(mode)

@jleveque jleveque merged commit 9d74aa0 into sonic-net:master Aug 14, 2018
stepanblyschak pushed a commit to stepanblyschak/sonic-utilities that referenced this pull request Apr 18, 2022
This PR updates the following commits in 202012

1f32e5c (HEAD -> 202012, origin/202012) [ycable][credo] Fix the is_link_active API for Credo Ycable (sonic-net#260)
c249681 [Y-Cable][Credo] add theading locker to support thread-safe calling, add SKU check for download_firmware API.  (sonic-net#222)

Signed-off-by: vaibhav-dahiya <vdahiya@microsoft.com>
mihirpat1 pushed a commit to mihirpat1/sonic-utilities that referenced this pull request Sep 15, 2023
…t#260)

Signed-off-by: vaibhav-dahiya vdahiya@microsoft.com

Description
Currently is_link_active API was broken because it was not using the target param in the routine to give back the correct link_status of ecah side. With this change it gives the correct result.

Motivation and Context
Required for MUX_CABLE_INFO table in streaming telemetry if the cable is disconnected or the link is down.

redis-cli -n 6 hgetall "MUX_CABLE_INFO|Ethernet16"
"
 9) "link_status_self"
10) "down"
11) "link_status_peer"
12) "up"
13) "link_status_nic"
14) "up"
How Has This Been Tested?
Tested on actual Arista7050cx3 testbed.
cyw233 pushed a commit to cyw233/sonic-utilities that referenced this pull request Mar 2, 2026
…#260

What I did
This PR introduces a new monitoring script to check synchronization of LAG (Link Aggregation Group) IDs between the chassis database and ASIC databases on VOQ chassis line cards. The script is designed to be run by Monit and will alert via syslog when mismatches are detected.
port sonic-net#4082 to 202405

How I did it
Key Changes
Added chassis_lag_id_checker script that retrieves and compares LAG IDs from chassis_db and asic_db, reporting mismatches per ASIC namespace
Comprehensive test suite with fixtures for mocking Redis dumps and ASIC/device configurations
Integration into setup.py for proper installation

How to verify it
test on voq chassis and UT
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants