Skip to content

Commit

Permalink
Common functions for show CLI support on multi ASIC (#999)
Browse files Browse the repository at this point in the history
Common changes will be used to support SONiC CLIs for multi ASIC
- New MultiAsic class  to support not displaying of internal object 
- Common CLI options which needed for multi ASIC platforms
- a new decorator to execute a function on all namespaces

Signed-off-by: Arvindsrinivasan Lakshmi Narasimhan <arlakshm@microsoft.com>
  • Loading branch information
arlakshm authored Aug 14, 2020
1 parent d5fdd74 commit a15b6bf
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 0 deletions.
9 changes: 9 additions & 0 deletions utilities_common/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#All the constant used in sonic-utilities

DEFAULT_NAMESPACE = ''
DISPLAY_ALL = 'all'
DISPLAY_EXTERNAL = 'frontend'
BGP_NEIGH_OBJ = 'BGP_NEIGH'
PORT_CHANNEL_OBJ = 'PORT_CHANNEL'
PORT_OBJ = 'PORT'

138 changes: 138 additions & 0 deletions utilities_common/multi_asic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import argparse
import functools

import click
from sonic_py_common import multi_asic
from utilities_common import constants


class MultiAsic(object):

def __init__(self, display_option=constants.DISPLAY_ALL,
namespace_option=None):
self.namespace_option = namespace_option
self.display_option = display_option
self.current_namespace = None
self.is_multi_asic = multi_asic.is_multi_asic()

def is_object_internal(self, object_type, cli_object):
'''
The function checks if a CLI object is internal and returns true or false.
Internal objects are port or portchannel which are connected to other
ports or portchannels within a multi ASIC device.
For single asic, this function is not applicable
'''
if object_type == constants.PORT_OBJ:
return multi_asic.is_port_internal(cli_object)
elif object_type == constants.PORT_CHANNEL_OBJ:
return multi_asic.is_port_channel_internal(cli_object)
elif object_type == constants.BGP_NEIGH_OBJ:
return multi_asic.is_bgp_session_internal(cli_object)

def skip_display(self, object_type, cli_object):
'''
The function determines if the passed cli_object has to be displayed or not.
returns true if the display_option is external and the cli object is internal.
returns false, if the cli option is all or if it the platform is single ASIC.
'''
if not self.is_multi_asic:
return False
if self.display_option == constants.DISPLAY_ALL:
return False
return self.is_object_internal(object_type, cli_object)

def get_ns_list_based_on_options(self):
ns_list = []
if not self.is_multi_asic:
return [constants.DEFAULT_NAMESPACE]
else:
namespaces = multi_asic.get_all_namespaces()
if self.namespace_option is None:
if self.display_option == constants.DISPLAY_ALL:
ns_list = namespaces['front_ns'] + namespaces['back_ns']
else:
ns_list = namespaces['front_ns']
else:
if self.namespace_option not in namespaces['front_ns'] and \
self.namespace_option not in namespaces['back_ns']:
raise ValueError(
'Unknown Namespace {}'.format(self.namespace_option))
ns_list = [self.namespace_option]
return ns_list


def multi_asic_ns_choices():
if not multi_asic.is_multi_asic():
return [constants.DEFAULT_NAMESPACE]
choices = multi_asic.get_namespace_list()
return choices


def multi_asic_display_choices():
if not multi_asic.is_multi_asic():
return [constants.DISPLAY_ALL]
else:
return [constants.DISPLAY_ALL, constants.DISPLAY_EXTERNAL]


def multi_asic_display_default_option():
if not multi_asic.is_multi_asic():
return constants.DISPLAY_ALL
else:
return constants.DISPLAY_EXTERNAL


_multi_asic_click_options = [
click.option('--display',
'-d', 'display',
default=multi_asic_display_default_option(),
show_default=True,
type=click.Choice(multi_asic_display_choices()),
help='Show internal interfaces'),
click.option('--namespace',
'-n', 'namespace',
default=None,
type=click.Choice(multi_asic_ns_choices()),
show_default=True,
help='Namespace name or all'),
]


def multi_asic_click_options(func):
for option in reversed(_multi_asic_click_options):
func = option(func)
return func


def run_on_multi_asic(func):
'''
This decorator is used on the CLI functions which needs to be
run on all the namespaces in the multi ASIC platform
The decorator loops through all the required namespaces,
for every iteration, it connects to all the DBs and provides an handle
to the wrapped function.
'''
@functools.wraps(func)
def wrapped_run_on_all_asics(self, *args, **kwargs):
ns_list = self.multi_asic.get_ns_list_based_on_options()
for ns in ns_list:
self.multi_asic.current_namespace = ns
self.db = multi_asic.connect_to_all_dbs_for_ns(ns)
self.config_db = multi_asic.connect_config_db_for_ns(ns)
func(self, *args, **kwargs)
return wrapped_run_on_all_asics


def multi_asic_args(parser=None):
if parser is None:
parser = argparse.ArgumentParser(
formatter_class=argparse.RawTextHelpFormatter)

parser.add_argument('-d', '--display', default=constants.DISPLAY_EXTERNAL,
help='Display all interfaces or only external interfaces')
parser.add_argument('-n', '--namespace', default=None,
help='Display interfaces for specific namespace')
return parser

0 comments on commit a15b6bf

Please sign in to comment.