diff --git a/config/aaa.py b/config/aaa.py index 251ada2652..8cdd818ac0 100644 --- a/config/aaa.py +++ b/config/aaa.py @@ -2,19 +2,9 @@ # -*- coding: utf-8 -*- import click -import netaddr -from swsssdk import ConfigDBConnector - - -def is_ipaddress(val): - if not val: - return False - try: - netaddr.IPAddress(str(val)) - except ValueError: - return False - return True +from swsssdk import ConfigDBConnector +import utilities_common.cli as clicommon def add_table_kv(table, entry, key, val): config_db = ConfigDBConnector() @@ -31,7 +21,6 @@ def del_table_key(table, entry, key): del data[key] config_db.set_entry(table, entry, data) - @click.group() def aaa(): """AAA command line""" @@ -164,7 +153,7 @@ def passkey(ctx, secret): @click.option('-m', '--use-mgmt-vrf', help="Management vrf, default is no vrf", is_flag=True) def add(address, timeout, key, auth_type, port, pri, use_mgmt_vrf): """Specify a TACACS+ server""" - if not is_ipaddress(address): + if not clicommon.is_ipaddress(address): click.echo('Invalid ip address') return @@ -196,7 +185,7 @@ def add(address, timeout, key, auth_type, port, pri, use_mgmt_vrf): @click.argument('address', metavar='') def delete(address): """Delete a TACACS+ server""" - if not is_ipaddress(address): + if not clicommon.is_ipaddress(address): click.echo('Invalid ip address') return diff --git a/config/main.py b/config/main.py index 2d917dd417..fc451f37b7 100755 --- a/config/main.py +++ b/config/main.py @@ -14,23 +14,25 @@ from minigraph import parse_device_desc_xml from portconfig import get_child_ports, get_port_config_file_name -from sonic_py_common import device_info, logger +from sonic_py_common import device_info from swsssdk import ConfigDBConnector, SonicV2Connector, SonicDBConfig from utilities_common.db import Db from utilities_common.intf_filter import parse_interface_in_filter -from utilities_common.cli import AbbreviationGroup, pass_db +import utilities_common.cli as clicommon +from .utils import log + import aaa import mlnx import nat import feature +import vlan from config_mgmt import ConfigMgmtDPB CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help', '-?']) SONIC_GENERATED_SERVICE_PATH = '/etc/sonic/generated_services.conf' SONIC_CFGGEN_PATH = '/usr/local/bin/sonic-cfggen' -SYSLOG_IDENTIFIER = "config" VLAN_SUB_INTERFACE_SEPARATOR = '.' ASIC_CONF_FILENAME = 'asic.conf' DEFAULT_CONFIG_DB_FILE = '/etc/sonic/config_db.json' @@ -51,9 +53,6 @@ CFG_LOOPBACK_NO="<0-999>" -# Global logger instance -log = logger.Logger(SYSLOG_IDENTIFIER) - asic_type = None # @@ -106,7 +105,7 @@ def shutdown_interfaces(ctx, del_intf_dict): """ shut down all the interfaces before deletion """ for intf in del_intf_dict.keys(): config_db = ctx.obj['config_db'] - if get_interface_naming_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interface_name = interface_alias_to_name(intf) if interface_name is None: click.echo("[ERROR] interface name is None!") @@ -215,7 +214,7 @@ def breakout_Ports(cm, delPorts=list(), portJson=dict(), force=False, \ def execute_systemctl_per_asic_instance(inst, event, service, action): try: click.echo("Executing {} of service {}@{}...".format(action, service, inst)) - run_command("systemctl {} {}@{}.service".format(action, service, inst)) + clicommon.run_command("systemctl {} {}@{}.service".format(action, service, inst)) except SystemExit as e: log.log_error("Failed to execute {} of service {}@{} with error {}".format(action, service, inst, e)) # Set the event object if there is a failure and exception was raised. @@ -234,7 +233,7 @@ def execute_systemctl(list_of_services, action): if (service + '.service' in generated_services_list): try: click.echo("Executing {} of service {}...".format(action, service)) - run_command("systemctl {} {}".format(action, service)) + clicommon.run_command("systemctl {} {}".format(action, service)) except SystemExit as e: log.log_error("Failed to execute {} of service {} with error {}".format(action, service, e)) raise @@ -260,21 +259,6 @@ def execute_systemctl(list_of_services, action): if e.is_set(): sys.exit(1) -def run_command(command, display_cmd=False, ignore_error=False): - """Run bash command and print output to stdout - """ - if display_cmd == True: - click.echo(click.style("Running command: ", fg='cyan') + click.style(command, fg='green')) - - proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) - (out, err) = proc.communicate() - - if len(out) > 0: - click.echo(out) - - if proc.returncode != 0 and not ignore_error: - sys.exit(proc.returncode) - def _get_device_type(): """ Get device type @@ -342,7 +326,7 @@ def interface_name_is_valid(interface_name): port_channel_dict = config_db.get_table('PORTCHANNEL') sub_port_intf_dict = config_db.get_table('VLAN_SUB_INTERFACE') - if get_interface_naming_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interface_name = interface_alias_to_name(interface_name) if interface_name is not None: @@ -482,12 +466,6 @@ def set_interface_naming_mode(mode): click.echo("Please logout and log back in for changes take effect.") -def get_interface_naming_mode(): - mode = os.getenv('SONIC_CLI_IFACE_MODE') - if mode is None: - mode = "default" - return mode - # Get the local BGP ASN from DEVICE_METADATA def get_local_bgp_asn(config_db): metadata = config_db.get_table('DEVICE_METADATA') @@ -580,10 +558,10 @@ def _remove_bgp_neighbor_config(config_db, neighbor_ip_or_hostname): def _change_hostname(hostname): current_hostname = os.uname()[1] if current_hostname != hostname: - run_command('echo {} > /etc/hostname'.format(hostname), display_cmd=True) - run_command('hostname -F /etc/hostname', display_cmd=True) - run_command('sed -i "/\s{}$/d" /etc/hosts'.format(current_hostname), display_cmd=True) - run_command('echo "127.0.0.1 {}" >> /etc/hosts'.format(hostname), display_cmd=True) + clicommon.run_command('echo {} > /etc/hostname'.format(hostname), display_cmd=True) + clicommon.run_command('hostname -F /etc/hostname', display_cmd=True) + clicommon.run_command('sed -i "/\s{}$/d" /etc/hosts'.format(current_hostname), display_cmd=True) + clicommon.run_command('echo "127.0.0.1 {}" >> /etc/hosts'.format(hostname), display_cmd=True) def _clear_qos(): QOS_TABLE_NAMES = [ @@ -751,16 +729,6 @@ def _restart_services(config_db): execute_systemctl(services_to_restart, SYSTEMCTL_ACTION_RESTART) -def is_ipaddress(val): - """ Validate if an entry is a valid IP """ - if not val: - return False - try: - netaddr.IPAddress(str(val)) - except ValueError: - return False - return True - def interface_is_in_vlan(vlan_member_table, interface_name): """ Check if an interface is in a vlan """ for _,intf in vlan_member_table.keys(): @@ -777,23 +745,6 @@ def interface_is_in_portchannel(portchannel_member_table, interface_name): return False -def interface_is_router_port(interface_table, interface_name): - """ Check if an interface has router config """ - for intf in interface_table.keys(): - if (interface_name == intf[0]): - return True - - return False - -def interface_is_mirror_dst_port(config_db, interface_name): - """ Check if port is already configured as mirror destination port """ - mirror_table = config_db.get_table('MIRROR_SESSION') - for _,v in mirror_table.items(): - if 'dst_port' in v and v['dst_port'] == interface_name: - return True - - return False - def interface_has_mirror_config(mirror_table, interface_name): """ Check if port is already configured with mirror config """ for _,v in mirror_table.items(): @@ -813,7 +764,6 @@ def validate_mirror_session_config(config_db, session_name, dst_port, src_port, vlan_member_table = config_db.get_table('VLAN_MEMBER') mirror_table = config_db.get_table('MIRROR_SESSION') portchannel_member_table = config_db.get_table('PORTCHANNEL_MEMBER') - interface_table = config_db.get_table('INTERFACE') if dst_port: if not interface_name_is_valid(dst_port): @@ -832,7 +782,7 @@ def validate_mirror_session_config(config_db, session_name, dst_port, src_port, click.echo("Error: Destination Interface {} has portchannel config".format(dst_port)) return False - if interface_is_router_port(interface_table, dst_port): + if clicommon.is_port_router_interface(config_db, dst_port): click.echo("Error: Destination Interface {} is a L3 interface".format(dst_port)) return False @@ -856,7 +806,7 @@ def validate_mirror_session_config(config_db, session_name, dst_port, src_port, return True # This is our main entrypoint - the main 'config' command -@click.group(cls=AbbreviationGroup, context_settings=CONTEXT_SETTINGS) +@click.group(cls=clicommon.AbbreviationGroup, context_settings=CONTEXT_SETTINGS) @click.pass_context def config(ctx): """SONiC command line - 'config' command""" @@ -889,6 +839,7 @@ def config(ctx): config.add_command(feature.feature) # === Add NAT Configuration ========== config.add_command(nat.nat) +config.add_command(vlan.vlan) @config.command() @click.option('-y', '--yes', is_flag=True, callback=_abort_if_false, @@ -938,7 +889,7 @@ def save(filename): command = "{} -n {} -d --print-data > {}".format(SONIC_CFGGEN_PATH, namespace, file) log.log_info("'save' executing...") - run_command(command, display_cmd=True) + clicommon.run_command(command, display_cmd=True) @config.command() @click.option('-y', '--yes', is_flag=True) @@ -1000,7 +951,7 @@ def load(filename, yes): command = "{} -n {} -j {} --write-to-db".format(SONIC_CFGGEN_PATH, namespace, file) log.log_info("'load' executing...") - run_command(command, display_cmd=True) + clicommon.run_command(command, display_cmd=True) @config.command() @@ -1008,7 +959,7 @@ def load(filename, yes): @click.option('-l', '--load-sysinfo', is_flag=True, help='load system default information (mac, portmap etc) first.') @click.option('-n', '--no_service_restart', default=False, is_flag=True, help='Do not restart docker services') @click.argument('filename', required=False) -@pass_db +@clicommon.pass_db def reload(db, filename, yes, load_sysinfo, no_service_restart): """Clear current configuration and import a previous saved config DB dump file. : Names of configuration file(s) to load, separated by comma with no spaces in between @@ -1092,7 +1043,7 @@ def reload(db, filename, yes, load_sysinfo, no_service_restart): command = "{} -H -k {} --write-to-db".format(SONIC_CFGGEN_PATH, cfg_hwsku) else: command = "{} -H -k {} -n {} --write-to-db".format(SONIC_CFGGEN_PATH, cfg_hwsku, namespace) - run_command(command, display_cmd=True) + clicommon.run_command(command, display_cmd=True) # For the database service running in linux host we use the file user gives as input # or by default DEFAULT_CONFIG_DB_FILE. In the case of database service running in namespace, @@ -1109,7 +1060,7 @@ def reload(db, filename, yes, load_sysinfo, no_service_restart): else: command = "{} -j {} -n {} --write-to-db".format(SONIC_CFGGEN_PATH, file, namespace) - run_command(command, display_cmd=True) + clicommon.run_command(command, display_cmd=True) client.set(config_db.INIT_INDICATOR, 1) # Migrate DB contents to latest version @@ -1119,7 +1070,7 @@ def reload(db, filename, yes, load_sysinfo, no_service_restart): command = "{} -o migrate".format(db_migrator) else: command = "{} -o migrate -n {}".format(db_migrator, namespace) - run_command(command, display_cmd=True) + clicommon.run_command(command, display_cmd=True) # We first run "systemctl reset-failed" to remove the "failed" # status from all services before we attempt to restart them @@ -1136,7 +1087,7 @@ def load_mgmt_config(filename): """Reconfigure hostname and mgmt interface based on device description file.""" log.log_info("'load_mgmt_config' executing...") command = "{} -M {} --write-to-db".format(SONIC_CFGGEN_PATH, filename) - run_command(command, display_cmd=True) + clicommon.run_command(command, display_cmd=True) #FIXME: After config DB daemon for hostname and mgmt interface is implemented, we'll no longer need to do manual configuration here config_data = parse_device_desc_xml(filename) hostname = config_data['DEVICE_METADATA']['localhost']['hostname'] @@ -1144,20 +1095,20 @@ def load_mgmt_config(filename): mgmt_conf = netaddr.IPNetwork(config_data['MGMT_INTERFACE'].keys()[0][1]) gw_addr = config_data['MGMT_INTERFACE'].values()[0]['gwaddr'] command = "ifconfig eth0 {} netmask {}".format(str(mgmt_conf.ip), str(mgmt_conf.netmask)) - run_command(command, display_cmd=True) + clicommon.run_command(command, display_cmd=True) command = "ip route add default via {} dev eth0 table default".format(gw_addr) - run_command(command, display_cmd=True, ignore_error=True) + clicommon.run_command(command, display_cmd=True, ignore_error=True) command = "ip rule add from {} table default".format(str(mgmt_conf.ip)) - run_command(command, display_cmd=True, ignore_error=True) + clicommon.run_command(command, display_cmd=True, ignore_error=True) command = "[ -f /var/run/dhclient.eth0.pid ] && kill `cat /var/run/dhclient.eth0.pid` && rm -f /var/run/dhclient.eth0.pid" - run_command(command, display_cmd=True, ignore_error=True) + clicommon.run_command(command, display_cmd=True, ignore_error=True) click.echo("Please note loaded setting will be lost after system reboot. To preserve setting, run `config save`.") @config.command("load_minigraph") @click.option('-y', '--yes', is_flag=True, callback=_abort_if_false, expose_value=False, prompt='Reload config from minigraph?') @click.option('-n', '--no_service_restart', default=False, is_flag=True, help='Do not restart docker services') -@pass_db +@clicommon.pass_db def load_minigraph(db, no_service_restart): """Reconfigure based on minigraph.""" log.log_info("'load_minigraph' executing...") @@ -1191,7 +1142,7 @@ def load_minigraph(db, no_service_restart): command = "{} -H -m -j /etc/sonic/init_cfg.json {} --write-to-db".format(SONIC_CFGGEN_PATH, cfggen_namespace_option) else: command = "{} -H -m --write-to-db {}".format(SONIC_CFGGEN_PATH, cfggen_namespace_option) - run_command(command, display_cmd=True) + clicommon.run_command(command, display_cmd=True) client.set(config_db.INIT_INDICATOR, 1) # get the device type @@ -1200,13 +1151,13 @@ def load_minigraph(db, no_service_restart): # These commands are not run for host on multi asic platform if num_npus == 1 or namespace is not DEFAULT_NAMESPACE: if device_type != 'MgmtToRRouter': - run_command('{}pfcwd start_default'.format(ns_cmd_prefix), display_cmd=True) + clicommon.run_command('{}pfcwd start_default'.format(ns_cmd_prefix), display_cmd=True) if os.path.isfile('/etc/sonic/acl.json'): - run_command("acl-loader update full /etc/sonic/acl.json", display_cmd=True) + clicommon.run_command("acl-loader update full /etc/sonic/acl.json", display_cmd=True) # generate QoS and Buffer configs - run_command("config qos reload", display_cmd=True) + clicommon.run_command("config qos reload", display_cmd=True) # Write latest db version string into db db_migrator='/usr/bin/db_migrator.py' @@ -1216,7 +1167,7 @@ def load_minigraph(db, no_service_restart): cfggen_namespace_option = " " else: cfggen_namespace_option = " -n {}".format(namespace) - run_command(db_migrator + ' -o set_version' + cfggen_namespace_option) + clicommon.run_command(db_migrator + ' -o set_version' + cfggen_namespace_option) # We first run "systemctl reset-failed" to remove the "failed" # status from all services before we attempt to restart them @@ -1241,7 +1192,7 @@ def hostname(new_hostname): config_db.mod_entry('DEVICE_METADATA' , 'localhost', {"hostname" : new_hostname}) try: command = "service hostname-config restart" - run_command(command, display_cmd=True) + clicommon.run_command(command, display_cmd=True) except SystemExit as e: click.echo("Restarting hostname-config service failed with error {}".format(e)) raise @@ -1250,7 +1201,7 @@ def hostname(new_hostname): # # 'portchannel' group ('config portchannel ...') # -@config.group(cls=AbbreviationGroup) +@config.group(cls=clicommon.AbbreviationGroup) @click.pass_context def portchannel(ctx): config_db = ConfigDBConnector() @@ -1281,7 +1232,7 @@ def remove_portchannel(ctx, portchannel_name): db = ctx.obj['db'] db.set_entry('PORTCHANNEL', portchannel_name, None) -@portchannel.group(cls=AbbreviationGroup, name='member') +@portchannel.group(cls=clicommon.AbbreviationGroup, name='member') @click.pass_context def portchannel_member(ctx): pass @@ -1293,7 +1244,7 @@ def portchannel_member(ctx): def add_portchannel_member(ctx, portchannel_name, port_name): """Add member to port channel""" db = ctx.obj['db'] - if interface_is_mirror_dst_port(db, port_name): + if clicommon.is_port_mirror_dst_port(db, port_name): ctx.fail("{} is configured as mirror destination port".format(port_name)) db.set_entry('PORTCHANNEL_MEMBER', (portchannel_name, port_name), {'NULL': 'NULL'}) @@ -1312,7 +1263,7 @@ def del_portchannel_member(ctx, portchannel_name, port_name): # # 'mirror_session' group ('config mirror_session ...') # -@config.group(cls=AbbreviationGroup, name='mirror_session') +@config.group(cls=clicommon.AbbreviationGroup, name='mirror_session') def mirror_session(): pass @@ -1333,7 +1284,7 @@ def add(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer): """ Add ERSPAN mirror session.(Legacy support) """ add_erspan(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer) -@mirror_session.group(cls=AbbreviationGroup, name='erspan') +@mirror_session.group(cls=clicommon.AbbreviationGroup, name='erspan') @click.pass_context def erspan(ctx): """ ERSPAN mirror_session """ @@ -1367,7 +1318,7 @@ def gather_session_info(session_info, policer, queue, src_port, direction): session_info['queue'] = queue if src_port: - if get_interface_naming_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": src_port_list = [] for port in src_port.split(","): src_port_list.append(interface_alias_to_name(port)) @@ -1413,7 +1364,7 @@ def add_erspan(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer return per_npu_configdb[front_asic_namespaces].set_entry("MIRROR_SESSION", session_name, session_info) -@mirror_session.group(cls=AbbreviationGroup, name='span') +@mirror_session.group(cls=clicommon.AbbreviationGroup, name='span') @click.pass_context def span(ctx): """ SPAN mirror session """ @@ -1431,7 +1382,7 @@ def add(session_name, dst_port, src_port, direction, queue, policer): add_span(session_name, dst_port, src_port, direction, queue, policer) def add_span(session_name, dst_port, src_port, direction, queue, policer): - if get_interface_naming_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": dst_port = interface_alias_to_name(dst_port) if dst_port is None: click.echo("Error: Destination Interface {} is invalid".format(dst_port)) @@ -1487,7 +1438,7 @@ def remove(session_name): # # 'pfcwd' group ('config pfcwd ...') # -@config.group(cls=AbbreviationGroup) +@config.group(cls=clicommon.AbbreviationGroup) def pfcwd(): """Configure pfc watchdog """ pass @@ -1520,7 +1471,7 @@ def start(action, restoration_time, ports, detection_time, verbose): if restoration_time: cmd += " --restoration-time {}".format(restoration_time) - run_command(cmd, display_cmd=verbose) + clicommon.run_command(cmd, display_cmd=verbose) @pfcwd.command() @click.option('--verbose', is_flag=True, help="Enable verbose output") @@ -1529,7 +1480,7 @@ def stop(verbose): cmd = "pfcwd stop" - run_command(cmd, display_cmd=verbose) + clicommon.run_command(cmd, display_cmd=verbose) @pfcwd.command() @click.option('--verbose', is_flag=True, help="Enable verbose output") @@ -1539,7 +1490,7 @@ def interval(poll_interval, verbose): cmd = "pfcwd interval {}".format(poll_interval) - run_command(cmd, display_cmd=verbose) + clicommon.run_command(cmd, display_cmd=verbose) @pfcwd.command('counter_poll') @click.option('--verbose', is_flag=True, help="Enable verbose output") @@ -1549,7 +1500,7 @@ def counter_poll(counter_poll, verbose): cmd = "pfcwd counter_poll {}".format(counter_poll) - run_command(cmd, display_cmd=verbose) + clicommon.run_command(cmd, display_cmd=verbose) @pfcwd.command('big_red_switch') @click.option('--verbose', is_flag=True, help="Enable verbose output") @@ -1559,7 +1510,7 @@ def big_red_switch(big_red_switch, verbose): cmd = "pfcwd big_red_switch {}".format(big_red_switch) - run_command(cmd, display_cmd=verbose) + clicommon.run_command(cmd, display_cmd=verbose) @pfcwd.command('start_default') @click.option('--verbose', is_flag=True, help="Enable verbose output") @@ -1568,12 +1519,12 @@ def start_default(verbose): cmd = "pfcwd start_default" - run_command(cmd, display_cmd=verbose) + clicommon.run_command(cmd, display_cmd=verbose) # # 'qos' group ('config qos ...') # -@config.group(cls=AbbreviationGroup) +@config.group(cls=clicommon.AbbreviationGroup) @click.pass_context def qos(ctx): """QoS-related configuration tasks""" @@ -1627,7 +1578,7 @@ def reload(): buffer_template_file, buffer_output_file ) - run_command(command, display_cmd=True) + clicommon.run_command(command, display_cmd=True) qos_template_file = os.path.join( hwsku_path, asic_id_suffix, @@ -1644,17 +1595,17 @@ def reload(): sonic_version_file, qos_output_file ) - run_command(command, display_cmd=True) + clicommon.run_command(command, display_cmd=True) # Apply the configurations only when both buffer and qos # configuration files are presented command = "{} {} -j {} --write-to-db".format( SONIC_CFGGEN_PATH, cmd_ns, buffer_output_file ) - run_command(command, display_cmd=True) + clicommon.run_command(command, display_cmd=True) command = "{} {} -j {} --write-to-db".format( SONIC_CFGGEN_PATH, cmd_ns, qos_output_file ) - run_command(command, display_cmd=True) + clicommon.run_command(command, display_cmd=True) else: click.secho('QoS definition template not found at {}'.format( qos_template_file @@ -1667,7 +1618,7 @@ def reload(): # # 'warm_restart' group ('config warm_restart ...') # -@config.group(cls=AbbreviationGroup, name='warm_restart') +@config.group(cls=clicommon.AbbreviationGroup, name='warm_restart') @click.pass_context @click.option('-s', '--redis-unix-socket-path', help='unix socket path for redis connection') def warm_restart(ctx, redis_unix_socket_path): @@ -1739,135 +1690,6 @@ def warm_restart_bgp_eoiu(ctx, enable): db = ctx.obj['db'] db.mod_entry('WARM_RESTART', 'bgp', {'bgp_eoiu': enable}) -# -# 'vlan' group ('config vlan ...') -# -@config.group(cls=AbbreviationGroup) -@click.pass_context -@click.option('-s', '--redis-unix-socket-path', help='unix socket path for redis connection') -def vlan(ctx, redis_unix_socket_path): - """VLAN-related configuration tasks""" - kwargs = {} - if redis_unix_socket_path: - kwargs['unix_socket_path'] = redis_unix_socket_path - config_db = ConfigDBConnector(**kwargs) - config_db.connect(wait_for_init=False) - ctx.obj = {'db': config_db} - -@vlan.command('add') -@click.argument('vid', metavar='', required=True, type=int) -@click.pass_context -def add_vlan(ctx, vid): - if vid >= 1 and vid <= 4094: - db = ctx.obj['db'] - vlan = 'Vlan{}'.format(vid) - if len(db.get_entry('VLAN', vlan)) != 0: - ctx.fail("{} already exists".format(vlan)) - db.set_entry('VLAN', vlan, {'vlanid': vid}) - else : - ctx.fail("Invalid VLAN ID {} (1-4094)".format(vid)) - -@vlan.command('del') -@click.argument('vid', metavar='', required=True, type=int) -@click.pass_context -def del_vlan(ctx, vid): - """Delete VLAN""" - log.log_info("'vlan del {}' executing...".format(vid)) - db = ctx.obj['db'] - keys = [ (k, v) for k, v in db.get_table('VLAN_MEMBER') if k == 'Vlan{}'.format(vid) ] - for k in keys: - db.set_entry('VLAN_MEMBER', k, None) - db.set_entry('VLAN', 'Vlan{}'.format(vid), None) - - -# -# 'member' group ('config vlan member ...') -# -@vlan.group(cls=AbbreviationGroup, name='member') -@click.pass_context -def vlan_member(ctx): - pass - - -@vlan_member.command('add') -@click.argument('vid', metavar='', required=True, type=int) -@click.argument('interface_name', metavar='', required=True) -@click.option('-u', '--untagged', is_flag=True) -@click.pass_context -def add_vlan_member(ctx, vid, interface_name, untagged): - """Add VLAN member""" - log.log_info("'vlan member add {} {}' executing...".format(vid, interface_name)) - db = ctx.obj['db'] - vlan_name = 'Vlan{}'.format(vid) - vlan = db.get_entry('VLAN', vlan_name) - interface_table = db.get_table('INTERFACE') - - if get_interface_naming_mode() == "alias": - interface_name = interface_alias_to_name(interface_name) - if interface_name is None: - ctx.fail("'interface_name' is None!") - - if len(vlan) == 0: - ctx.fail("{} doesn't exist".format(vlan_name)) - if interface_is_mirror_dst_port(db, interface_name): - ctx.fail("{} is configured as mirror destination port".format(interface_name)) - - members = vlan.get('members', []) - if interface_name in members: - if get_interface_naming_mode() == "alias": - interface_name = interface_name_to_alias(interface_name) - if interface_name is None: - ctx.fail("'interface_name' is None!") - ctx.fail("{} is already a member of {}".format(interface_name, - vlan_name)) - else: - ctx.fail("{} is already a member of {}".format(interface_name, - vlan_name)) - for entry in interface_table: - if (interface_name == entry[0]): - ctx.fail("{} is a L3 interface!".format(interface_name)) - - members.append(interface_name) - vlan['members'] = members - db.set_entry('VLAN', vlan_name, vlan) - db.set_entry('VLAN_MEMBER', (vlan_name, interface_name), {'tagging_mode': "untagged" if untagged else "tagged" }) - - -@vlan_member.command('del') -@click.argument('vid', metavar='', required=True, type=int) -@click.argument('interface_name', metavar='', required=True) -@click.pass_context -def del_vlan_member(ctx, vid, interface_name): - """Delete VLAN member""" - log.log_info("'vlan member del {} {}' executing...".format(vid, interface_name)) - db = ctx.obj['db'] - vlan_name = 'Vlan{}'.format(vid) - vlan = db.get_entry('VLAN', vlan_name) - - if get_interface_naming_mode() == "alias": - interface_name = interface_alias_to_name(interface_name) - if interface_name is None: - ctx.fail("'interface_name' is None!") - - if len(vlan) == 0: - ctx.fail("{} doesn't exist".format(vlan_name)) - members = vlan.get('members', []) - if interface_name not in members: - if get_interface_naming_mode() == "alias": - interface_name = interface_name_to_alias(interface_name) - if interface_name is None: - ctx.fail("'interface_name' is None!") - ctx.fail("{} is not a member of {}".format(interface_name, vlan_name)) - else: - ctx.fail("{} is not a member of {}".format(interface_name, vlan_name)) - members.remove(interface_name) - if len(members) == 0: - del vlan['members'] - else: - vlan['members'] = members - db.set_entry('VLAN', vlan_name, vlan) - db.set_entry('VLAN_MEMBER', (vlan_name, interface_name), None) - def mvrf_restart_services(): """Restart interfaces-config service and NTP service when mvrf is changed""" """ @@ -1905,7 +1727,7 @@ def vrf_delete_management_vrf(config_db): config_db.mod_entry('MGMT_VRF_CONFIG',"vrf_global",{"mgmtVrfEnabled": "false"}) mvrf_restart_services() -@config.group(cls=AbbreviationGroup) +@config.group(cls=clicommon.AbbreviationGroup) @click.pass_context def snmpagentaddress(ctx): """SNMP agent listening IP address, port, vrf configuration""" @@ -1954,7 +1776,7 @@ def del_snmp_agent_address(ctx, agentip, port, vrf): cmd="systemctl restart snmp" os.system (cmd) -@config.group(cls=AbbreviationGroup) +@config.group(cls=clicommon.AbbreviationGroup) @click.pass_context def snmptrap(ctx): """SNMP Trap server configuration to send traps""" @@ -2001,76 +1823,11 @@ def delete_snmptrap_server(ctx, ver): cmd="systemctl restart snmp" os.system (cmd) -@vlan.group(cls=AbbreviationGroup, name='dhcp_relay') -@click.pass_context -def vlan_dhcp_relay(ctx): - pass - -@vlan_dhcp_relay.command('add') -@click.argument('vid', metavar='', required=True, type=int) -@click.argument('dhcp_relay_destination_ip', metavar='', required=True) -@click.pass_context -def add_vlan_dhcp_relay_destination(ctx, vid, dhcp_relay_destination_ip): - """ Add a destination IP address to the VLAN's DHCP relay """ - if not is_ipaddress(dhcp_relay_destination_ip): - ctx.fail('Invalid IP address') - db = ctx.obj['db'] - vlan_name = 'Vlan{}'.format(vid) - vlan = db.get_entry('VLAN', vlan_name) - - if len(vlan) == 0: - ctx.fail("{} doesn't exist".format(vlan_name)) - dhcp_relay_dests = vlan.get('dhcp_servers', []) - if dhcp_relay_destination_ip in dhcp_relay_dests: - click.echo("{} is already a DHCP relay destination for {}".format(dhcp_relay_destination_ip, vlan_name)) - return - else: - dhcp_relay_dests.append(dhcp_relay_destination_ip) - vlan['dhcp_servers'] = dhcp_relay_dests - db.set_entry('VLAN', vlan_name, vlan) - click.echo("Added DHCP relay destination address {} to {}".format(dhcp_relay_destination_ip, vlan_name)) - try: - click.echo("Restarting DHCP relay service...") - run_command("systemctl restart dhcp_relay", display_cmd=False) - except SystemExit as e: - ctx.fail("Restart service dhcp_relay failed with error {}".format(e)) - -@vlan_dhcp_relay.command('del') -@click.argument('vid', metavar='', required=True, type=int) -@click.argument('dhcp_relay_destination_ip', metavar='', required=True) -@click.pass_context -def del_vlan_dhcp_relay_destination(ctx, vid, dhcp_relay_destination_ip): - """ Remove a destination IP address from the VLAN's DHCP relay """ - if not is_ipaddress(dhcp_relay_destination_ip): - ctx.fail('Invalid IP address') - db = ctx.obj['db'] - vlan_name = 'Vlan{}'.format(vid) - vlan = db.get_entry('VLAN', vlan_name) - - if len(vlan) == 0: - ctx.fail("{} doesn't exist".format(vlan_name)) - dhcp_relay_dests = vlan.get('dhcp_servers', []) - if dhcp_relay_destination_ip in dhcp_relay_dests: - dhcp_relay_dests.remove(dhcp_relay_destination_ip) - if len(dhcp_relay_dests) == 0: - del vlan['dhcp_servers'] - else: - vlan['dhcp_servers'] = dhcp_relay_dests - db.set_entry('VLAN', vlan_name, vlan) - click.echo("Removed DHCP relay destination address {} from {}".format(dhcp_relay_destination_ip, vlan_name)) - try: - click.echo("Restarting DHCP relay service...") - run_command("systemctl restart dhcp_relay", display_cmd=False) - except SystemExit as e: - ctx.fail("Restart service dhcp_relay failed with error {}".format(e)) - else: - ctx.fail("{} is not a DHCP relay destination for {}".format(dhcp_relay_destination_ip, vlan_name)) - # # 'bgp' group ('config bgp ...') # -@config.group(cls=AbbreviationGroup) +@config.group(cls=clicommon.AbbreviationGroup) def bgp(): """BGP-related configuration tasks""" pass @@ -2079,12 +1836,12 @@ def bgp(): # 'shutdown' subgroup ('config bgp shutdown ...') # -@bgp.group(cls=AbbreviationGroup) +@bgp.group(cls=clicommon.AbbreviationGroup) def shutdown(): """Shut down BGP session(s)""" pass -@config.group(cls=AbbreviationGroup) +@config.group(cls=clicommon.AbbreviationGroup) def kdump(): """ Configure kdump """ if os.geteuid() != 0: @@ -2097,7 +1854,7 @@ def disable(): if config_db is not None: config_db.connect() config_db.mod_entry("KDUMP", "config", {"enabled": "false"}) - run_command("sonic-kdump-config --disable") + clicommon.run_command("sonic-kdump-config --disable") @kdump.command() def enable(): @@ -2106,7 +1863,7 @@ def enable(): if config_db is not None: config_db.connect() config_db.mod_entry("KDUMP", "config", {"enabled": "true"}) - run_command("sonic-kdump-config --enable") + clicommon.run_command("sonic-kdump-config --enable") @kdump.command() @click.argument('kdump_memory', metavar='', required=True) @@ -2116,7 +1873,7 @@ def memory(kdump_memory): if config_db is not None: config_db.connect() config_db.mod_entry("KDUMP", "config", {"memory": kdump_memory}) - run_command("sonic-kdump-config --memory %s" % kdump_memory) + clicommon.run_command("sonic-kdump-config --memory %s" % kdump_memory) @kdump.command('num-dumps') @click.argument('kdump_num_dumps', metavar='', required=True, type=int) @@ -2126,7 +1883,7 @@ def num_dumps(kdump_num_dumps): if config_db is not None: config_db.connect() config_db.mod_entry("KDUMP", "config", {"num_dumps": kdump_num_dumps}) - run_command("sonic-kdump-config --num_dumps %d" % kdump_num_dumps) + clicommon.run_command("sonic-kdump-config --num_dumps %d" % kdump_num_dumps) # 'all' subcommand @shutdown.command() @@ -2180,7 +1937,7 @@ def neighbor(ipaddr_or_hostname, verbose): if not found_neighbor: click.get_current_context().fail("Could not locate neighbor '{}'".format(ipaddr_or_hostname)) -@bgp.group(cls=AbbreviationGroup) +@bgp.group(cls=clicommon.AbbreviationGroup) def startup(): """Start up BGP session(s)""" pass @@ -2241,7 +1998,7 @@ def neighbor(ipaddr_or_hostname, verbose): # 'remove' subgroup ('config bgp remove ...') # -@bgp.group(cls=AbbreviationGroup) +@bgp.group(cls=clicommon.AbbreviationGroup) def remove(): "Remove BGP neighbor configuration from the device" pass @@ -2274,7 +2031,7 @@ def remove_neighbor(neighbor_ip_or_hostname): # 'interface' group ('config interface ...') # -@config.group(cls=AbbreviationGroup) +@config.group(cls=clicommon.AbbreviationGroup) @click.pass_context def interface(ctx): """Interface-related configuration tasks""" @@ -2294,7 +2051,7 @@ def startup(ctx, interface_name): """Start up interface""" config_db = ctx.obj['config_db'] - if get_interface_naming_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interface_name = interface_alias_to_name(interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") @@ -2330,7 +2087,7 @@ def shutdown(ctx, interface_name): """Shut down interface""" log.log_info("'interface shutdown {}' executing...".format(interface_name)) config_db = ctx.obj['config_db'] - if get_interface_naming_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interface_name = interface_alias_to_name(interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") @@ -2365,7 +2122,7 @@ def shutdown(ctx, interface_name): @click.option('-v', '--verbose', is_flag=True, help="Enable verbose output") def speed(ctx, interface_name, interface_speed, verbose): """Set interface speed""" - if get_interface_naming_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interface_name = interface_alias_to_name(interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") @@ -2375,7 +2132,7 @@ def speed(ctx, interface_name, interface_speed, verbose): command = "portconfig -p {} -s {}".format(interface_name, interface_speed) if verbose: command += " -vv" - run_command(command, display_cmd=verbose) + clicommon.run_command(command, display_cmd=verbose) # # 'breakout' subcommand @@ -2528,7 +2285,7 @@ def mgmt_ip_restart_services(): @click.option('-v', '--verbose', is_flag=True, help="Enable verbose output") def mtu(ctx, interface_name, interface_mtu, verbose): """Set interface mtu""" - if get_interface_naming_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interface_name = interface_alias_to_name(interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") @@ -2536,7 +2293,7 @@ def mtu(ctx, interface_name, interface_mtu, verbose): command = "portconfig -p {} -m {}".format(interface_name, interface_mtu) if verbose: command += " -vv" - run_command(command, display_cmd=verbose) + clicommon.run_command(command, display_cmd=verbose) @interface.command() @click.pass_context @@ -2547,7 +2304,7 @@ def fec(ctx, interface_name, interface_fec, verbose): """Set interface fec""" if interface_fec not in ["rs", "fc", "none"]: ctx.fail("'fec not in ['rs', 'fc', 'none']!") - if get_interface_naming_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interface_name = interface_alias_to_name(interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") @@ -2555,13 +2312,13 @@ def fec(ctx, interface_name, interface_fec, verbose): command = "portconfig -p {} -f {}".format(interface_name, interface_fec) if verbose: command += " -vv" - run_command(command, display_cmd=verbose) + clicommon.run_command(command, display_cmd=verbose) # # 'ip' subgroup ('config interface ip ...') # -@interface.group(cls=AbbreviationGroup) +@interface.group(cls=clicommon.AbbreviationGroup) @click.pass_context def ip(ctx): """Add or remove IP address""" @@ -2579,7 +2336,7 @@ def ip(ctx): def add(ctx, interface_name, ip_addr, gw): """Add an IP address towards the interface""" config_db = ctx.obj["config_db"] - if get_interface_naming_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interface_name = interface_alias_to_name(interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") @@ -2638,7 +2395,7 @@ def add(ctx, interface_name, ip_addr, gw): def remove(ctx, interface_name, ip_addr): """Remove an IP address from the interface""" config_db = ctx.obj["config_db"] - if get_interface_naming_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interface_name = interface_alias_to_name(interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") @@ -2662,7 +2419,7 @@ def remove(ctx, interface_name, ip_addr): config_db.set_entry(table_name, interface_name, None) command = "ip neigh flush dev {} {}".format(interface_name, ip_addr) - run_command(command) + clicommon.run_command(command) except ValueError: ctx.fail("'ip_addr' is not valid.") @@ -2670,7 +2427,7 @@ def remove(ctx, interface_name, ip_addr): # 'transceiver' subgroup ('config interface transceiver ...') # -@interface.group(cls=AbbreviationGroup) +@interface.group(cls=clicommon.AbbreviationGroup) @click.pass_context def transceiver(ctx): """SFP transceiver configuration""" @@ -2686,7 +2443,7 @@ def transceiver(ctx): @click.pass_context def lpmode(ctx, interface_name, state): """Enable/disable low-power mode for SFP transceiver module""" - if get_interface_naming_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interface_name = interface_alias_to_name(interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") @@ -2695,7 +2452,7 @@ def lpmode(ctx, interface_name, state): ctx.fail("Interface name is invalid. Please enter a valid interface name!!") cmd = "sudo sfputil lpmode {} {}".format("on" if state == "enable" else "off", interface_name) - run_command(cmd) + clicommon.run_command(cmd) # # 'reset' subcommand ('config interface reset ...') @@ -2706,7 +2463,7 @@ def lpmode(ctx, interface_name, state): @click.pass_context def reset(ctx, interface_name): """Reset SFP transceiver module""" - if get_interface_naming_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interface_name = interface_alias_to_name(interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") @@ -2715,14 +2472,14 @@ def reset(ctx, interface_name): ctx.fail("Interface name is invalid. Please enter a valid interface name!!") cmd = "sudo sfputil reset {}".format(interface_name) - run_command(cmd) + clicommon.run_command(cmd) # # 'vrf' subgroup ('config interface vrf ...') # -@interface.group(cls=AbbreviationGroup) +@interface.group(cls=clicommon.AbbreviationGroup) @click.pass_context def vrf(ctx): """Bind or unbind VRF""" @@ -2738,7 +2495,7 @@ def vrf(ctx): def bind(ctx, interface_name, vrf_name): """Bind the interface to VRF""" config_db = ctx.obj["config_db"] - if get_interface_naming_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interface_name = interface_alias_to_name(interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") @@ -2773,7 +2530,7 @@ def bind(ctx, interface_name, vrf_name): def unbind(ctx, interface_name): """Unbind the interface to VRF""" config_db = ctx.obj["config_db"] - if get_interface_naming_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interface_name = interface_alias_to_name(interface_name) if interface_name is None: ctx.fail("interface is None!") @@ -2793,7 +2550,7 @@ def unbind(ctx, interface_name): # 'vrf' group ('config vrf ...') # -@config.group(cls=AbbreviationGroup, name='vrf') +@config.group(cls=clicommon.AbbreviationGroup, name='vrf') @click.pass_context def vrf(ctx): """VRF-related configuration tasks""" @@ -2838,7 +2595,7 @@ def del_vrf(ctx, vrf_name): # 'route' group ('config route ...') # -@config.group(cls=AbbreviationGroup) +@config.group(cls=clicommon.AbbreviationGroup) @click.pass_context def route(ctx): """route-related configuration tasks""" @@ -2894,7 +2651,7 @@ def add_route(ctx, command_str): else: ctx.fail("nexthop is not in pattern!") cmd += '"' - run_command(cmd) + clicommon.run_command(cmd) @route.command('del',context_settings={"ignore_unknown_options":True}) @click.argument('command_str', metavar='prefix [vrf ] nexthop <[vrf ] >|>', nargs=-1, type=click.Path()) @@ -2946,13 +2703,13 @@ def del_route(ctx, command_str): else: ctx.fail("nexthop is not in pattern!") cmd += '"' - run_command(cmd) + clicommon.run_command(cmd) # # 'acl' group ('config acl ...') # -@config.group(cls=AbbreviationGroup) +@config.group(cls=clicommon.AbbreviationGroup) def acl(): """ACL-related configuration tasks""" pass @@ -2961,7 +2718,7 @@ def acl(): # 'add' subgroup ('config acl add ...') # -@acl.group(cls=AbbreviationGroup) +@acl.group(cls=clicommon.AbbreviationGroup) def add(): """ Add ACL configuration. @@ -3025,7 +2782,7 @@ def table(table_name, table_type, description, ports, stage): # 'remove' subgroup ('config acl remove ...') # -@acl.group(cls=AbbreviationGroup) +@acl.group(cls=clicommon.AbbreviationGroup) def remove(): """ Remove ACL configuration. @@ -3051,7 +2808,7 @@ def table(table_name): # 'acl update' group # -@acl.group(cls=AbbreviationGroup) +@acl.group(cls=clicommon.AbbreviationGroup) def update(): """ACL-related configuration tasks""" pass @@ -3067,7 +2824,7 @@ def full(file_name): """Full update of ACL rules configuration.""" log.log_info("'acl update full {}' executing...".format(file_name)) command = "acl-loader update full {}".format(file_name) - run_command(command) + clicommon.run_command(command) # @@ -3080,14 +2837,14 @@ def incremental(file_name): """Incremental update of ACL rule configuration.""" log.log_info("'acl update incremental {}' executing...".format(file_name)) command = "acl-loader update incremental {}".format(file_name) - run_command(command) + clicommon.run_command(command) # # 'dropcounters' group ('config dropcounters ...') # -@config.group(cls=AbbreviationGroup) +@config.group(cls=clicommon.AbbreviationGroup) def dropcounters(): """Drop counter related configuration tasks""" pass @@ -3114,7 +2871,7 @@ def install(counter_name, alias, group, counter_type, desc, reasons, verbose): if desc: command += " -d '{}'".format(desc) - run_command(command, display_cmd=verbose) + clicommon.run_command(command, display_cmd=verbose) # @@ -3126,7 +2883,7 @@ def install(counter_name, alias, group, counter_type, desc, reasons, verbose): def delete(counter_name, verbose): """Delete an existing drop counter""" command = "dropconfig -c uninstall -n {}".format(counter_name) - run_command(command, display_cmd=verbose) + clicommon.run_command(command, display_cmd=verbose) # @@ -3139,7 +2896,7 @@ def delete(counter_name, verbose): def add_reasons(counter_name, reasons, verbose): """Add reasons to an existing drop counter""" command = "dropconfig -c add -n {} -r {}".format(counter_name, reasons) - run_command(command, display_cmd=verbose) + clicommon.run_command(command, display_cmd=verbose) # @@ -3152,7 +2909,7 @@ def add_reasons(counter_name, reasons, verbose): def remove_reasons(counter_name, reasons, verbose): """Remove reasons from an existing drop counter""" command = "dropconfig -c remove -n {} -r {}".format(counter_name, reasons) - run_command(command, display_cmd=verbose) + clicommon.run_command(command, display_cmd=verbose) # @@ -3178,14 +2935,14 @@ def ecn(profile, rmax, rmin, ymax, ymin, gmax, gmin, verbose): if gmax is not None: command += " -gmax %d" % gmax if gmin is not None: command += " -gmin %d" % gmin if verbose: command += " -vv" - run_command(command, display_cmd=verbose) + clicommon.run_command(command, display_cmd=verbose) # # 'pfc' group ('config interface pfc ...') # -@interface.group(cls=AbbreviationGroup) +@interface.group(cls=clicommon.AbbreviationGroup) @click.pass_context def pfc(ctx): """Set PFC configuration.""" @@ -3202,12 +2959,12 @@ def pfc(ctx): @click.pass_context def asymmetric(ctx, interface_name, status): """Set asymmetric PFC configuration.""" - if get_interface_naming_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interface_name = interface_alias_to_name(interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") - run_command("pfc config asymmetric {0} {1}".format(status, interface_name)) + clicommon.run_command("pfc config asymmetric {0} {1}".format(status, interface_name)) # # 'pfc priority' command ('config interface pfc priority ...') @@ -3220,23 +2977,23 @@ def asymmetric(ctx, interface_name, status): @click.pass_context def priority(ctx, interface_name, priority, status): """Set PFC priority configuration.""" - if get_interface_naming_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interface_name = interface_alias_to_name(interface_name) if interface_name is None: ctx.fail("'interface_name' is None!") - run_command("pfc config priority {0} {1} {2}".format(status, interface_name, priority)) + clicommon.run_command("pfc config priority {0} {1} {2}".format(status, interface_name, priority)) # # 'platform' group ('config platform ...') # -@config.group(cls=AbbreviationGroup) +@config.group(cls=clicommon.AbbreviationGroup) def platform(): """Platform-related configuration tasks""" # 'firmware' subgroup ("config platform firmware ...") -@platform.group(cls=AbbreviationGroup) +@platform.group(cls=clicommon.AbbreviationGroup) def firmware(): """Firmware configuration tasks""" pass @@ -3281,12 +3038,12 @@ def update(args): # 'watermark' group ("show watermark telemetry interval") # -@config.group(cls=AbbreviationGroup) +@config.group(cls=clicommon.AbbreviationGroup) def watermark(): """Configure watermark """ pass -@watermark.group(cls=AbbreviationGroup) +@watermark.group(cls=clicommon.AbbreviationGroup) def telemetry(): """Configure watermark telemetry""" pass @@ -3296,14 +3053,14 @@ def telemetry(): def interval(interval): """Configure watermark telemetry interval""" command = 'watermarkcfg --config-interval ' + interval - run_command(command) + clicommon.run_command(command) # # 'interface_naming_mode' subgroup ('config interface_naming_mode ...') # -@config.group(cls=AbbreviationGroup, name='interface_naming_mode') +@config.group(cls=clicommon.AbbreviationGroup, name='interface_naming_mode') def interface_naming_mode(): """Modify interface naming mode for interacting with SONiC CLI""" pass @@ -3383,7 +3140,7 @@ def del_loopback(ctx, loopback_name): config_db.set_entry('LOOPBACK_INTERFACE', loopback_name, None) -@config.group(cls=AbbreviationGroup) +@config.group(cls=clicommon.AbbreviationGroup) def ztp(): """ Configure Zero Touch Provisioning """ if os.path.isfile('/usr/bin/ztp') is False: @@ -3399,7 +3156,7 @@ def ztp(): def run(run): """Restart ZTP of the device.""" command = "ztp run -y" - run_command(command, display_cmd=True) + clicommon.run_command(command, display_cmd=True) @ztp.command() @click.option('-y', '--yes', is_flag=True, callback=_abort_if_false, @@ -3408,19 +3165,19 @@ def run(run): def disable(disable): """Administratively Disable ZTP.""" command = "ztp disable -y" - run_command(command, display_cmd=True) + clicommon.run_command(command, display_cmd=True) @ztp.command() @click.argument('enable', required=False, type=click.Choice(["enable"])) def enable(enable): """Administratively Enable ZTP.""" command = "ztp enable" - run_command(command, display_cmd=True) + clicommon.run_command(command, display_cmd=True) # # 'syslog' group ('config syslog ...') # -@config.group(cls=AbbreviationGroup, name='syslog') +@config.group(cls=clicommon.AbbreviationGroup, name='syslog') @click.pass_context def syslog_group(ctx): """Syslog server configuration tasks""" @@ -3433,7 +3190,7 @@ def syslog_group(ctx): @click.pass_context def add_syslog_server(ctx, syslog_ip_address): """ Add syslog server IP """ - if not is_ipaddress(syslog_ip_address): + if not clicommon.is_ipaddress(syslog_ip_address): ctx.fail('Invalid ip address') db = ctx.obj['db'] syslog_servers = db.get_table("SYSLOG_SERVER") @@ -3445,7 +3202,7 @@ def add_syslog_server(ctx, syslog_ip_address): click.echo("Syslog server {} added to configuration".format(syslog_ip_address)) try: click.echo("Restarting rsyslog-config service...") - run_command("systemctl restart rsyslog-config", display_cmd=False) + clicommon.run_command("systemctl restart rsyslog-config", display_cmd=False) except SystemExit as e: ctx.fail("Restart service rsyslog-config failed with error {}".format(e)) @@ -3454,7 +3211,7 @@ def add_syslog_server(ctx, syslog_ip_address): @click.pass_context def del_syslog_server(ctx, syslog_ip_address): """ Delete syslog server IP """ - if not is_ipaddress(syslog_ip_address): + if not clicommon.is_ipaddress(syslog_ip_address): ctx.fail('Invalid IP address') db = ctx.obj['db'] syslog_servers = db.get_table("SYSLOG_SERVER") @@ -3465,14 +3222,14 @@ def del_syslog_server(ctx, syslog_ip_address): ctx.fail("Syslog server {} is not configured.".format(syslog_ip_address)) try: click.echo("Restarting rsyslog-config service...") - run_command("systemctl restart rsyslog-config", display_cmd=False) + clicommon.run_command("systemctl restart rsyslog-config", display_cmd=False) except SystemExit as e: ctx.fail("Restart service rsyslog-config failed with error {}".format(e)) # # 'ntp' group ('config ntp ...') # -@config.group(cls=AbbreviationGroup) +@config.group(cls=clicommon.AbbreviationGroup) @click.pass_context def ntp(ctx): """NTP server configuration tasks""" @@ -3485,7 +3242,7 @@ def ntp(ctx): @click.pass_context def add_ntp_server(ctx, ntp_ip_address): """ Add NTP server IP """ - if not is_ipaddress(ntp_ip_address): + if not clicommon.is_ipaddress(ntp_ip_address): ctx.fail('Invalid ip address') db = ctx.obj['db'] ntp_servers = db.get_table("NTP_SERVER") @@ -3497,7 +3254,7 @@ def add_ntp_server(ctx, ntp_ip_address): click.echo("NTP server {} added to configuration".format(ntp_ip_address)) try: click.echo("Restarting ntp-config service...") - run_command("systemctl restart ntp-config", display_cmd=False) + clicommon.run_command("systemctl restart ntp-config", display_cmd=False) except SystemExit as e: ctx.fail("Restart service ntp-config failed with error {}".format(e)) @@ -3506,7 +3263,7 @@ def add_ntp_server(ctx, ntp_ip_address): @click.pass_context def del_ntp_server(ctx, ntp_ip_address): """ Delete NTP server IP """ - if not is_ipaddress(ntp_ip_address): + if not clicommon.is_ipaddress(ntp_ip_address): ctx.fail('Invalid IP address') db = ctx.obj['db'] ntp_servers = db.get_table("NTP_SERVER") @@ -3517,14 +3274,14 @@ def del_ntp_server(ctx, ntp_ip_address): ctx.fail("NTP server {} is not configured.".format(ntp_ip_address)) try: click.echo("Restarting ntp-config service...") - run_command("systemctl restart ntp-config", display_cmd=False) + clicommon.run_command("systemctl restart ntp-config", display_cmd=False) except SystemExit as e: ctx.fail("Restart service ntp-config failed with error {}".format(e)) # # 'sflow' group ('config sflow ...') # -@config.group(cls=AbbreviationGroup) +@config.group(cls=clicommon.AbbreviationGroup) @click.pass_context def sflow(ctx): """sFlow-related configuration tasks""" @@ -3557,8 +3314,8 @@ def enable(ctx): if out != "active": log.log_info("sflow service is not enabled. Starting sflow docker...") - run_command("sudo systemctl enable sflow") - run_command("sudo systemctl start sflow") + clicommon.run_command("sudo systemctl enable sflow") + clicommon.run_command("sudo systemctl start sflow") # # 'sflow' command ('config sflow disable') @@ -3605,7 +3362,7 @@ def is_valid_sample_rate(rate): # # 'sflow interface' group # -@sflow.group(cls=AbbreviationGroup) +@sflow.group(cls=clicommon.AbbreviationGroup) @click.pass_context def interface(ctx): """Configure sFlow settings for an interface""" @@ -3680,7 +3437,7 @@ def sample_rate(ctx, ifname, rate): # # 'sflow collector' group # -@sflow.group(cls=AbbreviationGroup) +@sflow.group(cls=clicommon.AbbreviationGroup) @click.pass_context def collector(ctx): """Add/Delete a sFlow collector""" @@ -3695,7 +3452,7 @@ def is_valid_collector_info(name, ip, port): click.echo("Collector port number must be between 0 and 65535") return False - if not is_ipaddress(ip): + if not clicommon.is_ipaddress(ip): click.echo("Invalid IP address") return False @@ -3748,7 +3505,7 @@ def del_collector(ctx, name): # # 'sflow agent-id' group # -@sflow.group(cls=AbbreviationGroup, name='agent-id') +@sflow.group(cls=clicommon.AbbreviationGroup, name='agent-id') @click.pass_context def agent_id(ctx): """Add/Delete a sFlow agent""" diff --git a/config/mlnx.py b/config/mlnx.py index 54775312ff..bd8c8c9425 100644 --- a/config/mlnx.py +++ b/config/mlnx.py @@ -7,12 +7,11 @@ try: import os - import subprocess - import sys import time import click from sonic_py_common import logger + import utilities_common.cli as clicommon except ImportError as e: raise ImportError("%s - required module not found" % str(e)) @@ -41,24 +40,6 @@ # Global logger instance log = logger.Logger(SNIFFER_SYSLOG_IDENTIFIER) - -# run command -def run_command(command, display_cmd=False, ignore_error=False): - """Run bash command and print output to stdout - """ - if display_cmd == True: - click.echo(click.style("Running command: ", fg='cyan') + click.style(command, fg='green')) - - proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) - (out, err) = proc.communicate() - - if len(out) > 0: - click.echo(out) - - if proc.returncode != 0 and not ignore_error: - sys.exit(proc.returncode) - - # generate sniffer target file name include a time stamp. def sniffer_filename_generate(path, filename_prefix, filename_ext): time_stamp = time.strftime("%Y%m%d%H%M%S") @@ -99,12 +80,12 @@ def env_variable_delete(delete_line): def conf_file_copy(src, dest): command = 'docker cp ' + src + ' ' + dest - run_command(command) + clicommon.run_command(command) def conf_file_receive(): command = "docker exec {} bash -c 'touch {}'".format(CONTAINER_NAME, SNIFFER_CONF_FILE) - run_command(command) + clicommon.run_command(command) conf_file_copy(SNIFFER_CONF_FILE_IN_CONTAINER, TMP_SNIFFER_CONF_FILE) @@ -134,7 +115,7 @@ def sniffer_env_variable_set(enable, env_variable_name, env_variable_string=""): config_file_send() command = 'rm -rf {}'.format(TMP_SNIFFER_CONF_FILE) - run_command(command) + clicommon.run_command(command) return ignore @@ -142,7 +123,7 @@ def sniffer_env_variable_set(enable, env_variable_name, env_variable_string=""): # restart the swss service with command 'service swss restart' def restart_swss(): try: - run_command(COMMAND_RESTART_SWSS) + clicommon.run_command(COMMAND_RESTART_SWSS) except OSError as e: log.log_error("Not able to restart swss service, %s" % str(e), True) return 1 diff --git a/config/utils.py b/config/utils.py new file mode 100644 index 0000000000..3cfcc37b43 --- /dev/null +++ b/config/utils.py @@ -0,0 +1,6 @@ +from sonic_py_common import logger + +SYSLOG_IDENTIFIER = "config" + +# Global logger instance +log = logger.Logger(SYSLOG_IDENTIFIER) diff --git a/config/vlan.py b/config/vlan.py new file mode 100644 index 0000000000..a43a374495 --- /dev/null +++ b/config/vlan.py @@ -0,0 +1,203 @@ +import click + +import utilities_common.cli as clicommon +from .utils import log + +# +# 'vlan' group ('config vlan ...') +# +@click.group(cls=clicommon.AbbreviationGroup, name='vlan') +def vlan(): + """VLAN-related configuration tasks""" + pass + +@vlan.command('add') +@click.argument('vid', metavar='', required=True, type=int) +@clicommon.pass_db +def add_vlan(db, vid): + """Add VLAN""" + + ctx = click.get_current_context() + + if not clicommon.is_vlanid_in_range(vid): + ctx.fail("Invalid VLAN ID {} (1-4094)".format(vid)) + + vlan = 'Vlan{}'.format(vid) + if clicommon.check_if_vlanid_exist(db.cfgdb, vlan): + ctx.fail("{} already exists".format(vlan)) + + db.cfgdb.set_entry('VLAN', vlan, {'vlanid': vid}) + +@vlan.command('del') +@click.argument('vid', metavar='', required=True, type=int) +@clicommon.pass_db +def del_vlan(db, vid): + """Delete VLAN""" + + log.log_info("'vlan del {}' executing...".format(vid)) + + ctx = click.get_current_context() + + if not clicommon.is_vlanid_in_range(vid): + ctx.fail("Invalid VLAN ID {} (1-4094)".format(vid)) + + vlan = 'Vlan{}'.format(vid) + if clicommon.check_if_vlanid_exist(db.cfgdb, vlan) == False: + ctx.fail("{} does not exist".format(vlan)) + + keys = [ (k, v) for k, v in db.cfgdb.get_table('VLAN_MEMBER') if k == 'Vlan{}'.format(vid) ] + for k in keys: + db.cfgdb.set_entry('VLAN_MEMBER', k, None) + db.cfgdb.set_entry('VLAN', 'Vlan{}'.format(vid), None) + +# +# 'member' group ('config vlan member ...') +# +@vlan.group(cls=clicommon.AbbreviationGroup, name='member') +def vlan_member(): + pass + +@vlan_member.command('add') +@click.argument('vid', metavar='', required=True, type=int) +@click.argument('port', metavar='port', required=True) +@click.option('-u', '--untagged', is_flag=True) +@clicommon.pass_db +def add_vlan_member(db, vid, port, untagged): + """Add VLAN member""" + + ctx = click.get_current_context() + + log.log_info("'vlan member add {} {}' executing...".format(vid, port)) + + if not clicommon.is_vlanid_in_range(vid): + ctx.fail("Invalid VLAN ID {} (1-4094)".format(vid)) + + vlan = 'Vlan{}'.format(vid) + if clicommon.check_if_vlanid_exist(db.cfgdb, vlan) == False: + ctx.fail("{} does not exist".format(vlan)) + + if clicommon.get_interface_naming_mode() == "alias": + alias = port + iface_alias_converter = clicommon.InterfaceAliasConverter(db) + port = iface_alias_converter.alias_to_name(alias) + if port is None: + ctx.fail("cannot find port name for alias {}".format(alias)) + + if clicommon.is_port_mirror_dst_port(db.cfgdb, port): + ctx.fail("{} is configured as mirror destination port".format(port)) + + if clicommon.is_port_vlan_member(db.cfgdb, port, vlan): + ctx.fail("{} is already a member of {}".format(port, vlan)) + + if clicommon.is_valid_port(db.cfgdb, port): + is_port = True + elif clicommon.is_valid_portchannel(db.cfgdb, port): + is_port = False + else: + ctx.fail("{} does not exist".format(port)) + + if (is_port and clicommon.is_port_router_interface(db.cfgdb, port)) or \ + (not is_port and clicommon.is_pc_router_interface(db.cfgdb, port)): + ctx.fail("{} is a router interface!".format(port)) + + db.cfgdb.set_entry('VLAN_MEMBER', (vlan, port), {'tagging_mode': "untagged" if untagged else "tagged" }) + +@vlan_member.command('del') +@click.argument('vid', metavar='', required=True, type=int) +@click.argument('port', metavar='', required=True) +@clicommon.pass_db +def del_vlan_member(db, vid, port): + """Delete VLAN member""" + + ctx = click.get_current_context() + + log.log_info("'vlan member del {} {}' executing...".format(vid, port)) + + if not clicommon.is_vlanid_in_range(vid): + ctx.fail("Invalid VLAN ID {} (1-4094)".format(vid)) + + vlan = 'Vlan{}'.format(vid) + if clicommon.check_if_vlanid_exist(db.cfgdb, vlan) == False: + ctx.fail("{} does not exist".format(vlan)) + + if clicommon.get_interface_naming_mode() == "alias": + alias = port + iface_alias_converter = clicommon.InterfaceAliasConverter(db) + port = iface_alias_converter.alias_to_name(alias) + if port is None: + ctx.fail("cannot find port name for alias {}".format(alias)) + + if not clicommon.is_port_vlan_member(db.cfgdb, port, vlan): + ctx.fail("{} is not a member of {}".format(port, vlan)) + + db.cfgdb.set_entry('VLAN_MEMBER', (vlan, port), None) + +@vlan.group(cls=clicommon.AbbreviationGroup, name='dhcp_relay') +def vlan_dhcp_relay(): + pass + +@vlan_dhcp_relay.command('add') +@click.argument('vid', metavar='', required=True, type=int) +@click.argument('dhcp_relay_destination_ip', metavar='', required=True) +@clicommon.pass_db +def add_vlan_dhcp_relay_destination(db, vid, dhcp_relay_destination_ip): + """ Add a destination IP address to the VLAN's DHCP relay """ + + ctx = click.get_current_context() + + if not clicommon.is_ipaddress(dhcp_relay_destination_ip): + ctx.fail('{} is invalid IP address'.format(dhcp_relay_destination_ip)) + + vlan_name = 'Vlan{}'.format(vid) + vlan = db.cfgdb.get_entry('VLAN', vlan_name) + if len(vlan) == 0: + ctx.fail("{} doesn't exist".format(vlan_name)) + + dhcp_relay_dests = vlan.get('dhcp_servers', []) + if dhcp_relay_destination_ip in dhcp_relay_dests: + click.echo("{} is already a DHCP relay destination for {}".format(dhcp_relay_destination_ip, vlan_name)) + return + + dhcp_relay_dests.append(dhcp_relay_destination_ip) + vlan['dhcp_servers'] = dhcp_relay_dests + db.cfgdb.set_entry('VLAN', vlan_name, vlan) + click.echo("Added DHCP relay destination address {} to {}".format(dhcp_relay_destination_ip, vlan_name)) + try: + click.echo("Restarting DHCP relay service...") + clicommon.run_command("systemctl restart dhcp_relay", display_cmd=False) + except SystemExit as e: + ctx.fail("Restart service dhcp_relay failed with error {}".format(e)) + +@vlan_dhcp_relay.command('del') +@click.argument('vid', metavar='', required=True, type=int) +@click.argument('dhcp_relay_destination_ip', metavar='', required=True) +@clicommon.pass_db +def del_vlan_dhcp_relay_destination(db, vid, dhcp_relay_destination_ip): + """ Remove a destination IP address from the VLAN's DHCP relay """ + + ctx = click.get_current_context() + + if not clicommon.is_ipaddress(dhcp_relay_destination_ip): + ctx.fail('{} is invalid IP address'.format(dhcp_relay_destination_ip)) + + vlan_name = 'Vlan{}'.format(vid) + vlan = db.cfgdb.get_entry('VLAN', vlan_name) + if len(vlan) == 0: + ctx.fail("{} doesn't exist".format(vlan_name)) + + dhcp_relay_dests = vlan.get('dhcp_servers', []) + if not dhcp_relay_destination_ip in dhcp_relay_dests: + ctx.fail("{} is not a DHCP relay destination for {}".format(dhcp_relay_destination_ip, vlan_name)) + + dhcp_relay_dests.remove(dhcp_relay_destination_ip) + if len(dhcp_relay_dests) == 0: + del vlan['dhcp_servers'] + else: + vlan['dhcp_servers'] = dhcp_relay_dests + db.cfgdb.set_entry('VLAN', vlan_name, vlan) + click.echo("Removed DHCP relay destination address {} from {}".format(dhcp_relay_destination_ip, vlan_name)) + try: + click.echo("Restarting DHCP relay service...") + clicommon.run_command("systemctl restart dhcp_relay", display_cmd=False) + except SystemExit as e: + ctx.fail("Restart service dhcp_relay failed with error {}".format(e)) diff --git a/show/bgp_frr_v4.py b/show/bgp_frr_v4.py index 5f2831c05a..9d2fc57dd4 100644 --- a/show/bgp_frr_v4.py +++ b/show/bgp_frr_v4.py @@ -1,5 +1,8 @@ import click -from show.main import AliasedGroup, ip, run_command, get_bgp_summary_extended + +import utilities_common.cli as clicommon + +from show.main import ip, run_command, get_bgp_summary_extended ############################################################################### @@ -9,7 +12,7 @@ ############################################################################### -@ip.group(cls=AliasedGroup) +@ip.group(cls=clicommon.AliasedGroup) def bgp(): """Show IPv4 BGP (Border Gateway Protocol) information""" pass diff --git a/show/bgp_frr_v6.py b/show/bgp_frr_v6.py index f199ac60b9..37d13a8690 100644 --- a/show/bgp_frr_v6.py +++ b/show/bgp_frr_v6.py @@ -1,5 +1,7 @@ import click -from show.main import AliasedGroup, ipv6, run_command, get_bgp_summary_extended + +import utilities_common.cli as clicommon +from show.main import ipv6, run_command, get_bgp_summary_extended ############################################################################### @@ -9,7 +11,7 @@ ############################################################################### -@ipv6.group(cls=AliasedGroup) +@ipv6.group(cls=clicommon.AliasedGroup) def bgp(): """Show IPv6 BGP (Border Gateway Protocol) information""" pass diff --git a/show/main.py b/show/main.py index ba7e912dde..44399729ce 100755 --- a/show/main.py +++ b/show/main.py @@ -19,9 +19,11 @@ from swsssdk import SonicV2Connector from tabulate import tabulate from utilities_common.db import Db +import utilities_common.cli as clicommon -import feature import mlnx +import vlan +import feature # Global Variables PLATFORM_JSON = 'platform.json' @@ -30,139 +32,6 @@ VLAN_SUB_INTERFACE_SEPARATOR = '.' -try: - # noinspection PyPep8Naming - import ConfigParser as configparser -except ImportError: - # noinspection PyUnresolvedReferences - import configparser - - -# This is from the aliases example: -# https://github.com/pallets/click/blob/57c6f09611fc47ca80db0bd010f05998b3c0aa95/examples/aliases/aliases.py -class Config(object): - """Object to hold CLI config""" - - def __init__(self): - self.path = os.getcwd() - self.aliases = {} - - def read_config(self, filename): - parser = configparser.RawConfigParser() - parser.read([filename]) - try: - self.aliases.update(parser.items('aliases')) - except configparser.NoSectionError: - pass - -class InterfaceAliasConverter(object): - """Class which handles conversion between interface name and alias""" - - def __init__(self): - self.alias_max_length = 0 - - config_db = ConfigDBConnector() - config_db.connect() - self.port_dict = config_db.get_table('PORT') - - if not self.port_dict: - click.echo(message="Warning: failed to retrieve PORT table from ConfigDB!", err=True) - self.port_dict = {} - - for port_name in self.port_dict.keys(): - try: - if self.alias_max_length < len( - self.port_dict[port_name]['alias']): - self.alias_max_length = len( - self.port_dict[port_name]['alias']) - except KeyError: - break - - def name_to_alias(self, interface_name): - """Return vendor interface alias if SONiC - interface name is given as argument - """ - vlan_id = '' - sub_intf_sep_idx = -1 - if interface_name is not None: - sub_intf_sep_idx = interface_name.find(VLAN_SUB_INTERFACE_SEPARATOR) - if sub_intf_sep_idx != -1: - vlan_id = interface_name[sub_intf_sep_idx + 1:] - # interface_name holds the parent port name - interface_name = interface_name[:sub_intf_sep_idx] - - for port_name in self.port_dict.keys(): - if interface_name == port_name: - return self.port_dict[port_name]['alias'] if sub_intf_sep_idx == -1 \ - else self.port_dict[port_name]['alias'] + VLAN_SUB_INTERFACE_SEPARATOR + vlan_id - - # interface_name not in port_dict. Just return interface_name - return interface_name if sub_intf_sep_idx == -1 else interface_name + VLAN_SUB_INTERFACE_SEPARATOR + vlan_id - - def alias_to_name(self, interface_alias): - """Return SONiC interface name if vendor - port alias is given as argument - """ - vlan_id = '' - sub_intf_sep_idx = -1 - if interface_alias is not None: - sub_intf_sep_idx = interface_alias.find(VLAN_SUB_INTERFACE_SEPARATOR) - if sub_intf_sep_idx != -1: - vlan_id = interface_alias[sub_intf_sep_idx + 1:] - # interface_alias holds the parent port alias - interface_alias = interface_alias[:sub_intf_sep_idx] - - for port_name in self.port_dict.keys(): - if interface_alias == self.port_dict[port_name]['alias']: - return port_name if sub_intf_sep_idx == -1 else port_name + VLAN_SUB_INTERFACE_SEPARATOR + vlan_id - - # interface_alias not in port_dict. Just return interface_alias - return interface_alias if sub_intf_sep_idx == -1 else interface_alias + VLAN_SUB_INTERFACE_SEPARATOR + vlan_id - - -# Global Config object -_config = None - - -class AliasedGroup(click.Group): - """This subclass of click.Group supports abbreviations and - looking up aliases in a config file with a bit of magic. - """ - - def get_command(self, ctx, cmd_name): - global _config - - # If we haven't instantiated our global config, do it now and load current config - if _config is None: - _config = Config() - - # Load our config file - cfg_file = os.path.join(os.path.dirname(__file__), 'aliases.ini') - _config.read_config(cfg_file) - - # Try to get builtin commands as normal - rv = click.Group.get_command(self, ctx, cmd_name) - if rv is not None: - return rv - - # No builtin found. Look up an explicit command alias in the config - if cmd_name in _config.aliases: - actual_cmd = _config.aliases[cmd_name] - return click.Group.get_command(self, ctx, actual_cmd) - - # Alternative option: if we did not find an explicit alias we - # allow automatic abbreviation of the command. "status" for - # instance will match "st". We only allow that however if - # there is only one command. - matches = [x for x in self.list_commands(ctx) - if x.lower().startswith(cmd_name.lower())] - if not matches: - return None - elif len(matches) == 1: - return click.Group.get_command(self, ctx, matches[0]) - ctx.fail('Too many matches: %s' % ', '.join(sorted(matches))) - - # To be enhanced. Routing-stack information should be collected from a global # location (configdb?), so that we prevent the continous execution of this # bash oneliner. To be revisited once routing-stack info is tracked somewhere. @@ -203,7 +72,7 @@ def run_command(command, display_cmd=False, return_cmd=False): # No conversion needed for intfutil commands as it already displays # both SONiC interface name and alias name for all interfaces. - if get_interface_mode() == "alias" and not command.startswith("intfutil"): + if clicommon.get_interface_naming_mode() == "alias" and not command.startswith("intfutil"): run_command_in_alias_mode(command) raise sys.exit(0) @@ -223,26 +92,8 @@ def run_command(command, display_cmd=False, return_cmd=False): if rc != 0: sys.exit(rc) - -def get_interface_mode(): - mode = os.getenv('SONIC_CLI_IFACE_MODE') - if mode is None: - mode = "default" - return mode - - -def is_ip_prefix_in_key(key): - ''' - Function to check if IP address is present in the key. If it - is present, then the key would be a tuple or else, it shall be - be string - ''' - return (isinstance(key, tuple)) - - # Global class instance for SONiC interface name to alias conversion -iface_alias_converter = InterfaceAliasConverter() - +iface_alias_converter = clicommon.InterfaceAliasConverter() def print_output_in_alias_mode(output, index): """Convert and print all instances of SONiC interface @@ -553,7 +404,7 @@ def get_bgp_neighbor_ip_to_name(ip, static_neighbors, dynamic_neighbors): # This is our entrypoint - the main "show" command # TODO: Consider changing function name to 'show' for better understandability -@click.group(cls=AliasedGroup, context_settings=CONTEXT_SETTINGS) +@click.group(cls=clicommon.AliasedGroup, context_settings=CONTEXT_SETTINGS) @click.pass_context def cli(ctx): """SONiC command line - 'show' command""" @@ -561,6 +412,7 @@ def cli(ctx): ctx.obj = Db() cli.add_command(feature.feature) +cli.add_command(vlan.vlan) # # 'vrf' command ("show vrf") @@ -620,7 +472,7 @@ def arp(ipaddress, iface, verbose): cmd += " -ip {}".format(ipaddress) if iface is not None: - if get_interface_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": if not ((iface.startswith("PortChannel")) or (iface.startswith("eth"))): iface = iface_alias_converter.alias_to_name(iface) @@ -700,7 +552,7 @@ def mgmt_vrf(ctx,routes): # 'management_interface' group ("show management_interface ...") # -@cli.group(name='management_interface', cls=AliasedGroup) +@cli.group(name='management_interface', cls=clicommon.AliasedGroup) def management_interface(): """Show management interface parameters""" pass @@ -766,7 +618,7 @@ def snmptrap (ctx): # 'interfaces' group ("show interfaces ...") # -@cli.group(cls=AliasedGroup) +@cli.group(cls=clicommon.AliasedGroup) def interfaces(): """Show details of the network interfaces""" pass @@ -786,7 +638,7 @@ def alias(interfacename): body = [] if interfacename is not None: - if get_interface_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interfacename = iface_alias_converter.alias_to_name(interfacename) # If we're given an interface name, output name and alias for that interface only @@ -901,7 +753,7 @@ def currrent_mode(ctx, interface): # # 'neighbor' group ### # -@interfaces.group(cls=AliasedGroup) +@interfaces.group(cls=clicommon.AliasedGroup) def neighbor(): """Show neighbor related information""" pass @@ -931,7 +783,7 @@ def expected(interfacename): device2interface_dict = {} for port in natsorted(neighbor_dict['DEVICE_NEIGHBOR'].keys()): temp_port = port - if get_interface_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": port = iface_alias_converter.name_to_alias(port) neighbor_dict['DEVICE_NEIGHBOR'][port] = neighbor_dict['DEVICE_NEIGHBOR'].pop(temp_port) device2interface_dict[neighbor_dict['DEVICE_NEIGHBOR'][port]['name']] = {'localPort': port, 'neighborPort': neighbor_dict['DEVICE_NEIGHBOR'][port]['port']} @@ -958,7 +810,7 @@ def expected(interfacename): click.echo(tabulate(body, header)) -@interfaces.group(cls=AliasedGroup) +@interfaces.group(cls=clicommon.AliasedGroup) def transceiver(): """Show SFP Transceiver information""" pass @@ -977,7 +829,7 @@ def eeprom(interfacename, dump_dom, verbose): cmd += " --dom" if interfacename is not None: - if get_interface_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interfacename = iface_alias_converter.alias_to_name(interfacename) cmd += " -p {}".format(interfacename) @@ -994,7 +846,7 @@ def lpmode(interfacename, verbose): cmd = "sudo sfputil show lpmode" if interfacename is not None: - if get_interface_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interfacename = iface_alias_converter.alias_to_name(interfacename) cmd += " -p {}".format(interfacename) @@ -1010,7 +862,7 @@ def presence(interfacename, verbose): cmd = "sfpshow presence" if interfacename is not None: - if get_interface_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interfacename = iface_alias_converter.alias_to_name(interfacename) cmd += " -p {}".format(interfacename) @@ -1027,7 +879,7 @@ def description(interfacename, verbose): cmd = "intfutil description" if interfacename is not None: - if get_interface_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interfacename = iface_alias_converter.alias_to_name(interfacename) cmd += " {}".format(interfacename) @@ -1044,7 +896,7 @@ def status(interfacename, verbose): cmd = "intfutil status" if interfacename is not None: - if get_interface_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interfacename = iface_alias_converter.alias_to_name(interfacename) cmd += " {}".format(interfacename) @@ -1124,7 +976,7 @@ def portchannel(verbose): # 'subinterfaces' group ("show subinterfaces ...") # -@cli.group(cls=AliasedGroup) +@cli.group(cls=clicommon.AliasedGroup) def subinterfaces(): """Show details of the sub port interfaces""" pass @@ -1143,7 +995,7 @@ def status(subinterfacename, verbose): print("Invalid sub port interface name") return - if get_interface_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": subinterfacename = iface_alias_converter.alias_to_name(subinterfacename) cmd += subinterfacename @@ -1155,7 +1007,7 @@ def status(subinterfacename, verbose): # 'pfc' group ("show pfc ...") # -@cli.group(cls=AliasedGroup) +@cli.group(cls=clicommon.AliasedGroup) def pfc(): """Show details of the priority-flow-control (pfc) """ pass @@ -1175,7 +1027,7 @@ def counters(verbose): def priority(interface): """Show pfc priority""" cmd = 'pfc show priority' - if interface is not None and get_interface_mode() == "alias": + if interface is not None and clicommon.get_interface_naming_mode() == "alias": interface = iface_alias_converter.alias_to_name(interface) if interface is not None: @@ -1188,7 +1040,7 @@ def priority(interface): def asymmetric(interface): """Show asymmetric pfc""" cmd = 'pfc show asymmetric' - if interface is not None and get_interface_mode() == "alias": + if interface is not None and clicommon.get_interface_naming_mode() == "alias": interface = iface_alias_converter.alias_to_name(interface) if interface is not None: @@ -1197,7 +1049,7 @@ def asymmetric(interface): run_command(cmd) # 'pfcwd' subcommand ("show pfcwd...") -@cli.group(cls=AliasedGroup) +@cli.group(cls=clicommon.AliasedGroup) def pfcwd(): """Show details of the pfc watchdog """ pass @@ -1226,14 +1078,14 @@ def stats(verbose): def naming_mode(verbose): """Show interface naming_mode status""" - click.echo(get_interface_mode()) + click.echo(clicommon.get_interface_naming_mode()) # # 'watermark' group ("show watermark telemetry interval") # -@cli.group(cls=AliasedGroup) +@cli.group(cls=clicommon.AliasedGroup) def watermark(): """Show details of watermark """ pass @@ -1254,7 +1106,7 @@ def show_tm_interval(): # 'queue' group ("show queue ...") # -@cli.group(cls=AliasedGroup) +@cli.group(cls=clicommon.AliasedGroup) def queue(): """Show details of the queues """ pass @@ -1269,7 +1121,7 @@ def counters(interfacename, verbose): cmd = "queuestat" if interfacename is not None: - if get_interface_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interfacename = iface_alias_converter.alias_to_name(interfacename) if interfacename is not None: @@ -1328,7 +1180,7 @@ def pwm_q_multi(): # 'priority-group' group ("show priority-group ...") # -@cli.group(name='priority-group', cls=AliasedGroup) +@cli.group(name='priority-group', cls=clicommon.AliasedGroup) def priority_group(): """Show details of the PGs """ @@ -1371,7 +1223,7 @@ def pwm_pg_shared(): # 'buffer_pool' group ("show buffer_pool ...") # -@cli.group(name='buffer_pool', cls=AliasedGroup) +@cli.group(name='buffer_pool', cls=clicommon.AliasedGroup) def buffer_pool(): """Show details of the buffer pools""" @@ -1429,7 +1281,7 @@ def route_map(route_map_name, verbose): # # This group houses IP (i.e., IPv4) commands and subgroups -@cli.group(cls=AliasedGroup) +@cli.group(cls=clicommon.AliasedGroup) def ip(): """Show IP (IPv4) commands""" pass @@ -1534,7 +1386,7 @@ def interfaces(): else: oper = "down" master = get_if_master(iface) - if get_interface_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": iface = iface_alias_converter.name_to_alias(iface) data.append([iface, master, ifaddresses[0][1], admin + "/" + oper, neighbor_name, neighbor_ip]) @@ -1612,7 +1464,7 @@ def protocol(verbose): # # This group houses IPv6-related commands and subgroups -@cli.group(cls=AliasedGroup) +@cli.group(cls=clicommon.AliasedGroup) def ipv6(): """Show IPv6 commands""" pass @@ -1676,7 +1528,7 @@ def interfaces(): else: oper = "down" master = get_if_master(iface) - if get_interface_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": iface = iface_alias_converter.name_to_alias(iface) data.append([iface, master, ifaddresses[0][1], admin + "/" + oper, neighbor_info[0][0], neighbor_info[0][1]]) neighbor_info.pop(0) @@ -1734,7 +1586,7 @@ def protocol(verbose): # 'lldp' group ("show lldp ...") # -@cli.group(cls=AliasedGroup) +@cli.group(cls=clicommon.AliasedGroup) def lldp(): """LLDP (Link Layer Discovery Protocol) information""" pass @@ -1748,7 +1600,7 @@ def neighbors(interfacename, verbose): cmd = "sudo lldpshow -d" if interfacename is not None: - if get_interface_mode() == "alias": + if clicommon.get_interface_naming_mode() == "alias": interfacename = iface_alias_converter.alias_to_name(interfacename) cmd += " -p {}".format(interfacename) @@ -1781,7 +1633,7 @@ def get_hw_info_dict(): return hw_info_dict -@cli.group(cls=AliasedGroup) +@cli.group(cls=clicommon.AliasedGroup) def platform(): """Show platform-specific hardware info""" pass @@ -1951,7 +1803,7 @@ def environment(verbose): # 'processes' group ("show processes ...") # -@cli.group(cls=AliasedGroup) +@cli.group(cls=clicommon.AliasedGroup) def processes(): """Display process information""" pass @@ -2014,7 +1866,7 @@ def techsupport(since, verbose): # 'runningconfiguration' group ("show runningconfiguration") # -@cli.group(cls=AliasedGroup) +@cli.group(cls=clicommon.AliasedGroup) def runningconfiguration(): """Show current running configuration information""" pass @@ -2128,7 +1980,7 @@ def syslog(verbose): # 'startupconfiguration' group ("show startupconfiguration ...") # -@cli.group(cls=AliasedGroup) +@cli.group(cls=clicommon.AliasedGroup) def startupconfiguration(): """Show startup configuration information""" pass @@ -2202,15 +2054,10 @@ def system_memory(verbose): cmd = "free -m" run_command(cmd, display_cmd=verbose) -@cli.group(cls=AliasedGroup) -def vlan(): - """Show VLAN information""" - pass - # # 'kdump command ("show kdump ...") # -@cli.group(cls=AliasedGroup) +@cli.group(cls=clicommon.AliasedGroup) def kdump(): """Show kdump configuration, status and information """ pass @@ -2288,134 +2135,6 @@ def log(record, lines): else: run_command("sonic-kdump-config --file %s --lines %s" % (record, lines)) -@vlan.command() -@click.option('--verbose', is_flag=True, help="Enable verbose output") -def brief(verbose): - """Show all bridge information""" - config_db = ConfigDBConnector() - config_db.connect() - header = ['VLAN ID', 'IP Address', 'Ports', 'Port Tagging', 'DHCP Helper Address'] - body = [] - vlan_keys = [] - - # Fetching data from config_db for VLAN, VLAN_INTERFACE and VLAN_MEMBER - vlan_dhcp_helper_data = config_db.get_table('VLAN') - vlan_ip_data = config_db.get_table('VLAN_INTERFACE') - vlan_ports_data = config_db.get_table('VLAN_MEMBER') - - # Defining dictionaries for DHCP Helper address, Interface Gateway IP, - # VLAN ports and port tagging - vlan_dhcp_helper_dict = {} - vlan_ip_dict = {} - vlan_ports_dict = {} - vlan_tagging_dict = {} - - # Parsing DHCP Helpers info - for key in natsorted(vlan_dhcp_helper_data.keys()): - try: - if vlan_dhcp_helper_data[key]['dhcp_servers']: - vlan_dhcp_helper_dict[str(key.strip('Vlan'))] = vlan_dhcp_helper_data[key]['dhcp_servers'] - except KeyError: - vlan_dhcp_helper_dict[str(key.strip('Vlan'))] = " " - - # Parsing VLAN Gateway info - for key in natsorted(vlan_ip_data.keys()): - if not is_ip_prefix_in_key(key): - continue - interface_key = str(key[0].strip("Vlan")) - interface_value = str(key[1]) - if interface_key in vlan_ip_dict: - vlan_ip_dict[interface_key].append(interface_value) - else: - vlan_ip_dict[interface_key] = [interface_value] - - # Parsing VLAN Ports info - for key in natsorted(vlan_ports_data.keys()): - ports_key = str(key[0].strip("Vlan")) - ports_value = str(key[1]) - ports_tagging = vlan_ports_data[key]['tagging_mode'] - if ports_key in vlan_ports_dict: - if get_interface_mode() == "alias": - ports_value = iface_alias_converter.name_to_alias(ports_value) - vlan_ports_dict[ports_key].append(ports_value) - else: - if get_interface_mode() == "alias": - ports_value = iface_alias_converter.name_to_alias(ports_value) - vlan_ports_dict[ports_key] = [ports_value] - if ports_key in vlan_tagging_dict: - vlan_tagging_dict[ports_key].append(ports_tagging) - else: - vlan_tagging_dict[ports_key] = [ports_tagging] - - # Printing the following dictionaries in tablular forms: - # vlan_dhcp_helper_dict={}, vlan_ip_dict = {}, vlan_ports_dict = {} - # vlan_tagging_dict = {} - for key in natsorted(vlan_dhcp_helper_dict.keys()): - if key not in vlan_ip_dict: - ip_address = "" - else: - ip_address = ','.replace(',', '\n').join(vlan_ip_dict[key]) - if key not in vlan_ports_dict: - vlan_ports = "" - else: - vlan_ports = ','.replace(',', '\n').join((vlan_ports_dict[key])) - if key not in vlan_dhcp_helper_dict: - dhcp_helpers = "" - else: - dhcp_helpers = ','.replace(',', '\n').join(vlan_dhcp_helper_dict[key]) - if key not in vlan_tagging_dict: - vlan_tagging = "" - else: - vlan_tagging = ','.replace(',', '\n').join((vlan_tagging_dict[key])) - body.append([key, ip_address, vlan_ports, vlan_tagging, dhcp_helpers]) - click.echo(tabulate(body, header, tablefmt="grid")) - -@vlan.command() -@click.option('-s', '--redis-unix-socket-path', help='unix socket path for redis connection') -def config(redis_unix_socket_path): - kwargs = {} - if redis_unix_socket_path: - kwargs['unix_socket_path'] = redis_unix_socket_path - config_db = ConfigDBConnector(**kwargs) - config_db.connect(wait_for_init=False) - data = config_db.get_table('VLAN') - keys = data.keys() - - def tablelize(keys, data): - table = [] - - for k in natsorted(keys): - if 'members' not in data[k] : - r = [] - r.append(k) - r.append(data[k]['vlanid']) - table.append(r) - continue - - for m in data[k].get('members', []): - r = [] - r.append(k) - r.append(data[k]['vlanid']) - if get_interface_mode() == "alias": - alias = iface_alias_converter.name_to_alias(m) - r.append(alias) - else: - r.append(m) - - entry = config_db.get_entry('VLAN_MEMBER', (k, m)) - mode = entry.get('tagging_mode') - if mode is None: - r.append('?') - else: - r.append(mode) - - table.append(r) - - return table - - header = ['Name', 'VID', 'Member', 'Mode'] - click.echo(tabulate(tablelize(keys, data), header)) - @cli.command('services') def services(): """Show all daemon services""" @@ -2606,7 +2325,7 @@ def show_sflow_global(config_db): # 'acl' group ### # -@cli.group(cls=AliasedGroup) +@cli.group(cls=clicommon.AliasedGroup) def acl(): """Show ACL related information""" pass @@ -2648,7 +2367,7 @@ def table(table_name, verbose): # 'dropcounters' group ### # -@cli.group(cls=AliasedGroup) +@cli.group(cls=clicommon.AliasedGroup) def dropcounters(): """Show drop counter related information""" pass @@ -2760,7 +2479,7 @@ def line(verbose): return -@cli.group(name='warm_restart', cls=AliasedGroup) +@cli.group(name='warm_restart', cls=clicommon.AliasedGroup) def warm_restart(): """Show warm restart configuration and state""" pass @@ -2886,7 +2605,7 @@ def tablelize(keys, data, enable_table_keys, prefix): # 'nat' group ("show nat ...") # -@cli.group(cls=AliasedGroup) +@cli.group(cls=clicommon.AliasedGroup) def nat(): """Show details of the nat """ pass @@ -2965,13 +2684,13 @@ def pool(verbose): app_db.connect(app_db.APPL_DB) if app_db.keys(app_db.APPL_DB, '_GEARBOX_TABLE:phy:*'): - @cli.group(cls=AliasedGroup) + @cli.group(cls=clicommon.AliasedGroup) def gearbox(): """Show gearbox info""" pass # 'phys' subcommand ("show gearbox phys") - @gearbox.group(cls=AliasedGroup) + @gearbox.group(cls=clicommon.AliasedGroup) def phys(): """Show external PHY information""" pass @@ -2985,7 +2704,7 @@ def status(ctx): return # 'interfaces' subcommand ("show gearbox interfaces") - @gearbox.group(cls=AliasedGroup) + @gearbox.group(cls=clicommon.AliasedGroup) def interfaces(): """Show gearbox interfaces information""" pass @@ -3044,7 +2763,7 @@ def ztp(status, verbose): # # 'vnet' command ("show vnet") # -@cli.group(cls=AliasedGroup) +@cli.group(cls=clicommon.AliasedGroup) def vnet(): """Show vnet related information""" pass @@ -3301,7 +3020,7 @@ def tunnel(): # # 'vxlan' command ("show vxlan") # -@cli.group(cls=AliasedGroup) +@cli.group(cls=clicommon.AliasedGroup) def vxlan(): """Show vxlan related information""" pass diff --git a/show/vlan.py b/show/vlan.py new file mode 100644 index 0000000000..d40f93cd29 --- /dev/null +++ b/show/vlan.py @@ -0,0 +1,133 @@ +import click +from natsort import natsorted +from tabulate import tabulate + +import utilities_common.cli as clicommon + +@click.group(cls=clicommon.AliasedGroup) +def vlan(): + """Show VLAN information""" + pass + +@vlan.command() +@click.option('--verbose', is_flag=True, help="Enable verbose output") +@clicommon.pass_db +def brief(db, verbose): + """Show all bridge information""" + header = ['VLAN ID', 'IP Address', 'Ports', 'Port Tagging', 'DHCP Helper Address'] + body = [] + + # Fetching data from config db for VLAN, VLAN_INTERFACE and VLAN_MEMBER + vlan_dhcp_helper_data = db.cfgdb.get_table('VLAN') + vlan_ip_data = db.cfgdb.get_table('VLAN_INTERFACE') + vlan_ports_data = db.cfgdb.get_table('VLAN_MEMBER') + + # Defining dictionaries for DHCP Helper address, Interface Gateway IP, + # VLAN ports and port tagging + vlan_dhcp_helper_dict = {} + vlan_ip_dict = {} + vlan_ports_dict = {} + vlan_tagging_dict = {} + + # Parsing DHCP Helpers info + for key in natsorted(vlan_dhcp_helper_data.keys()): + try: + if vlan_dhcp_helper_data[key]['dhcp_servers']: + vlan_dhcp_helper_dict[str(key.strip('Vlan'))] = vlan_dhcp_helper_data[key]['dhcp_servers'] + except KeyError: + vlan_dhcp_helper_dict[str(key.strip('Vlan'))] = " " + + # Parsing VLAN Gateway info + for key in natsorted(vlan_ip_data.keys()): + if not clicommon.is_ip_prefix_in_key(key): + continue + interface_key = str(key[0].strip("Vlan")) + interface_value = str(key[1]) + if interface_key in vlan_ip_dict: + vlan_ip_dict[interface_key].append(interface_value) + else: + vlan_ip_dict[interface_key] = [interface_value] + + iface_alias_converter = clicommon.InterfaceAliasConverter(db) + + # Parsing VLAN Ports info + for key in natsorted(vlan_ports_data.keys()): + ports_key = str(key[0].strip("Vlan")) + ports_value = str(key[1]) + ports_tagging = vlan_ports_data[key]['tagging_mode'] + if ports_key in vlan_ports_dict: + if clicommon.get_interface_naming_mode() == "alias": + ports_value = iface_alias_converter.name_to_alias(ports_value) + vlan_ports_dict[ports_key].append(ports_value) + else: + if clicommon.get_interface_naming_mode() == "alias": + ports_value = iface_alias_converter.name_to_alias(ports_value) + vlan_ports_dict[ports_key] = [ports_value] + if ports_key in vlan_tagging_dict: + vlan_tagging_dict[ports_key].append(ports_tagging) + else: + vlan_tagging_dict[ports_key] = [ports_tagging] + + # Printing the following dictionaries in tablular forms: + # vlan_dhcp_helper_dict={}, vlan_ip_dict = {}, vlan_ports_dict = {} + # vlan_tagging_dict = {} + for key in natsorted(vlan_dhcp_helper_dict.keys()): + if key not in vlan_ip_dict: + ip_address = "" + else: + ip_address = ','.replace(',', '\n').join(vlan_ip_dict[key]) + if key not in vlan_ports_dict: + vlan_ports = "" + else: + vlan_ports = ','.replace(',', '\n').join((vlan_ports_dict[key])) + if key not in vlan_dhcp_helper_dict: + dhcp_helpers = "" + else: + dhcp_helpers = ','.replace(',', '\n').join(vlan_dhcp_helper_dict[key]) + if key not in vlan_tagging_dict: + vlan_tagging = "" + else: + vlan_tagging = ','.replace(',', '\n').join((vlan_tagging_dict[key])) + body.append([key, ip_address, vlan_ports, vlan_tagging, dhcp_helpers]) + click.echo(tabulate(body, header, tablefmt="grid")) + +@vlan.command() +@clicommon.pass_db +def config(db): + data = db.cfgdb.get_table('VLAN') + keys = data.keys() + + def tablelize(keys, data): + table = [] + + for k in natsorted(keys): + if 'members' not in data[k] : + r = [] + r.append(k) + r.append(data[k]['vlanid']) + table.append(r) + continue + + for m in data[k].get('members', []): + r = [] + r.append(k) + r.append(data[k]['vlanid']) + if clicommon.get_interface_naming_mode() == "alias": + alias = iface_alias_converter.name_to_alias(m) + r.append(alias) + else: + r.append(m) + + entry = db.cfgdb.get_entry('VLAN_MEMBER', (k, m)) + mode = entry.get('tagging_mode') + if mode is None: + r.append('?') + else: + r.append(mode) + + table.append(r) + + return table + + header = ['Name', 'VID', 'Member', 'Mode'] + click.echo(tabulate(tablelize(keys, data), header)) diff --git a/tests/config_test.py b/tests/config_test.py index ebc0b55fdf..e3c519da6a 100644 --- a/tests/config_test.py +++ b/tests/config_test.py @@ -1,3 +1,4 @@ +import os import traceback from click.testing import CliRunner @@ -48,6 +49,7 @@ class TestLoadMinigraph(object): @classmethod def setup_class(cls): + os.environ['UTILITIES_UNIT_TESTING'] = "1" print("SETUP") def test_load_minigraph(self, get_cmd_module, setup_single_broacom_asic): @@ -77,4 +79,5 @@ def test_load_minigraph_with_disabled_telemetry(self, get_cmd_module, setup_sing @classmethod def teardown_class(cls): + os.environ['UTILITIES_UNIT_TESTING'] = "0" print("TEARDOWN") diff --git a/tests/conftest.py b/tests/conftest.py index fae575fdc7..e46e6605fd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,7 +2,6 @@ import sys import mock -import click import pytest import mock_tables.dbconnector @@ -48,18 +47,11 @@ 'snmp.timer', 'telemetry.timer'] - -def _dummy_run_command(command, display_cmd=False, return_cmd=False): - if display_cmd == True: - click.echo(click.style("Running command: ", fg='cyan') + click.style(command, fg='green')) - @pytest.fixture def get_cmd_module(): import config.main as config import show.main as show - config.run_command = _dummy_run_command - return (config, show) @pytest.fixture diff --git a/tests/intfutil_test.py b/tests/intfutil_test.py index 67f6f9b6a8..1fd5fe777e 100644 --- a/tests/intfutil_test.py +++ b/tests/intfutil_test.py @@ -10,6 +10,22 @@ modules_path = os.path.dirname(root_path) scripts_path = os.path.join(modules_path, "scripts") +show_interface_status_output="""\ + Interface Lanes Speed MTU FEC Alias Vlan Oper Admin Type Asym PFC +--------------- --------------- ------- ----- ----- --------- --------------- ------ ------- --------------- ---------- + Ethernet0 0 25G 9100 rs Ethernet0 routed down up QSFP28 or later off + Ethernet32 13,14,15,16 40G 9100 rs etp9 PortChannel1001 up up N/A off + Ethernet112 93,94,95,96 40G 9100 rs etp29 PortChannel0001 up up N/A off + Ethernet116 89,90,91,92 40G 9100 rs etp30 PortChannel0002 up up N/A off + Ethernet120 101,102,103,104 40G 9100 rs etp31 PortChannel0003 up up N/A off + Ethernet124 97,98,99,100 40G 9100 rs etp32 PortChannel0004 up up N/A off +PortChannel0001 N/A 40G 9100 N/A N/A routed N/A N/A N/A N/A +PortChannel0002 N/A 40G 9100 N/A N/A routed N/A N/A N/A N/A +PortChannel0003 N/A 40G 9100 N/A N/A routed N/A N/A N/A N/A +PortChannel0004 N/A 40G 9100 N/A N/A routed N/A N/A N/A N/A +PortChannel1001 N/A 40G 9100 N/A N/A routed N/A N/A N/A N/A +""" + class TestIntfutil(TestCase): @classmethod def setup_class(cls): @@ -26,17 +42,12 @@ def test_intf_status(self): # Test 'show interfaces status' result = self.runner.invoke(show.cli.commands["interfaces"].commands["status"], []) print >> sys.stderr, result.output - expected_output = ( - "Interface Lanes Speed MTU FEC Alias Vlan Oper Admin Type Asym PFC\n" - "----------- ------- ------- ----- ----- --------- ------ ------ ------- --------------- ----------\n" - " Ethernet0 0 25G 9100 rs Ethernet0 routed down up QSFP28 or later off" - ) - self.assertEqual(result.output.strip(), expected_output) + assert result.output == show_interface_status_output # Test 'intfutil status' output = subprocess.check_output('intfutil status', stderr=subprocess.STDOUT, shell=True) print >> sys.stderr, output - self.assertEqual(output.strip(), expected_output) + assert result.output == show_interface_status_output # Test 'show interfaces status --verbose' def test_intf_status_verbose(self): @@ -45,7 +56,6 @@ def test_intf_status_verbose(self): expected_output = "Command: intfutil status" self.assertEqual(result.output.split('\n')[0], expected_output) - # Test 'show subinterfaces status' / 'intfutil status subport' def test_subintf_status(self): # Test 'show subinterfaces status' diff --git a/tests/mock_tables/appl_db.json b/tests/mock_tables/appl_db.json index 4239cf949d..44bd9dd3f6 100644 --- a/tests/mock_tables/appl_db.json +++ b/tests/mock_tables/appl_db.json @@ -11,6 +11,66 @@ "fec": "rs", "admin_status": "up" }, + "PORT_TABLE:Ethernet32": { + "index": "8", + "lanes": "13,14,15,16", + "alias": "etp9", + "description": "Servers7:eth0", + "speed": "40000", + "oper_status": "up", + "pfc_asym": "off", + "mtu": "9100", + "fec": "rs", + "admin_status": "up" + }, + "PORT_TABLE:Ethernet112": { + "index": "28", + "lanes": "93,94,95,96", + "alias": "etp29", + "description": "ARISTA01T1:Ethernet1", + "speed": "40000", + "oper_status": "up", + "pfc_asym": "off", + "mtu": "9100", + "fec": "rs", + "admin_status": "up" + }, + "PORT_TABLE:Ethernet116": { + "index": "29", + "lanes": "89,90,91,92", + "alias": "etp30", + "description": "ARISTA02T1:Ethernet1", + "speed": "40000", + "oper_status": "up", + "pfc_asym": "off", + "mtu": "9100", + "fec": "rs", + "admin_status": "up" + }, + "PORT_TABLE:Ethernet120": { + "index": "30", + "lanes": "101,102,103,104", + "alias": "etp31", + "description": "ARISTA03T1:Ethernet1", + "speed": "40000", + "oper_status": "up", + "pfc_asym": "off", + "mtu": "9100", + "fec": "rs", + "admin_status": "up" + }, + "PORT_TABLE:Ethernet124": { + "index": "31", + "lanes": "97,98,99,100", + "alias": "etp32", + "description": "ARISTA04T1:Ethernet1", + "speed": "40000", + "oper_status": "up", + "pfc_asym": "off", + "mtu": "9100", + "fec": "rs", + "admin_status": "up" + }, "PORT_TABLE:Ethernet200": { "index": "200", "lanes": "200,201,202,203", diff --git a/tests/mock_tables/config_db.json b/tests/mock_tables/config_db.json index f60ee3ef12..0dc8939c12 100644 --- a/tests/mock_tables/config_db.json +++ b/tests/mock_tables/config_db.json @@ -10,130 +10,475 @@ }, "PORT|Ethernet0": { "alias": "etp1", - "lanes": "0,1,2,3", + "description": "etp1", + "index": "0", + "lanes": "25,26,27,28", "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet4": { + "admin_status": "up", + "alias": "etp2", + "description": "Servers0:eth0", + "index": "1", + "lanes": "29,30,31,32", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet8": { + "admin_status": "up", + "alias": "etp3", + "description": "Servers1:eth0", + "index": "2", + "lanes": "33,34,35,36", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet12": { + "admin_status": "up", + "alias": "etp4", + "description": "Servers2:eth0", + "index": "3", + "lanes": "37,38,39,40", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet16": { + "admin_status": "up", + "alias": "etp5", + "description": "Servers3:eth0", + "index": "4", + "lanes": "45,46,47,48", + "mtu": "9100", + "pfc_asym": "off", "speed": "40000" }, "PORT|Ethernet20": { + "admin_status": "up", "alias": "etp6", - "lanes": "20,21,22,23", + "description": "Servers4:eth0", + "index": "5", + "lanes": "41,42,43,44", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet24": { + "admin_status": "up", + "alias": "etp7", + "description": "Servers5:eth0", + "index": "6", + "lanes": "1,2,3,4", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet28": { + "admin_status": "up", + "alias": "etp8", + "description": "Servers6:eth0", + "index": "7", + "lanes": "5,6,7,8", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet32": { + "admin_status": "up", + "alias": "etp9", + "description": "Servers7:eth0", + "index": "8", + "lanes": "13,14,15,16", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet36": { + "admin_status": "up", + "alias": "etp10", + "description": "Servers8:eth0", + "index": "9", + "lanes": "9,10,11,12", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet40": { + "admin_status": "up", + "alias": "etp11", + "description": "Servers9:eth0", + "index": "10", + "lanes": "17,18,19,20", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet44": { + "admin_status": "up", + "alias": "etp12", + "description": "Servers10:eth0", + "index": "11", + "lanes": "21,22,23,24", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet48": { + "admin_status": "up", + "alias": "etp13", + "description": "Servers11:eth0", + "index": "12", + "lanes": "53,54,55,56", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet52": { + "admin_status": "up", + "alias": "etp14", + "description": "Servers12:eth0", + "index": "13", + "lanes": "49,50,51,52", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet56": { + "admin_status": "up", + "alias": "etp15", + "description": "Servers13:eth0", + "index": "14", + "lanes": "57,58,59,60", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet60": { + "admin_status": "up", + "alias": "etp16", + "description": "Servers14:eth0", + "index": "15", + "lanes": "61,62,63,64", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet64": { + "admin_status": "up", + "alias": "etp17", + "description": "Servers15:eth0", + "index": "16", + "lanes": "69,70,71,72", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet68": { + "admin_status": "up", + "alias": "etp18", + "description": "Servers16:eth0", + "index": "17", + "lanes": "65,66,67,68", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet72": { + "admin_status": "up", + "alias": "etp19", + "description": "Servers17:eth0", + "index": "18", + "lanes": "73,74,75,76", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet76": { + "admin_status": "up", + "alias": "etp20", + "description": "Servers18:eth0", + "index": "19", + "lanes": "77,78,79,80", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet80": { + "admin_status": "up", + "alias": "etp21", + "description": "Servers19:eth0", + "index": "20", + "lanes": "109,110,111,112", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet84": { + "admin_status": "up", + "alias": "etp22", + "description": "Servers20:eth0", + "index": "21", + "lanes": "105,106,107,108", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet88": { + "admin_status": "up", + "alias": "etp23", + "description": "Servers21:eth0", + "index": "22", + "lanes": "113,114,115,116", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet92": { + "admin_status": "up", + "alias": "etp24", + "description": "Servers22:eth0", + "index": "23", + "lanes": "117,118,119,120", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet96": { + "admin_status": "up", + "alias": "etp25", + "description": "Servers23:eth0", + "index": "24", + "lanes": "125,126,127,128", "mtu": "9100", + "pfc_asym": "off", "speed": "40000" }, "PORT|Ethernet100": { "alias": "etp26", - "lanes": "100,101,102,103", + "description": "fortyGigE0/100", + "index": "25", + "lanes": "121,122,123,124", "mtu": "9100", + "pfc_asym": "off", "speed": "40000" }, "PORT|Ethernet104": { "alias": "etp27", - "lanes": "104,105,106,107", - "mtu": "9100" + "description": "fortyGigE0/104", + "index": "26", + "lanes": "81,82,83,84", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" }, "PORT|Ethernet108": { "alias": "etp28", - "lanes": "108,109,110,111", + "description": "fortyGigE0/108", + "index": "27", + "lanes": "85,86,87,88", "mtu": "9100", + "pfc_asym": "off", "speed": "40000" }, "PORT|Ethernet112": { "admin_status": "up", "alias": "etp29", - "lanes": "112,113,114,115", + "description": "ARISTA01T1:PORT|Ethernet1", + "index": "28", + "lanes": "93,94,95,96", "mtu": "9100", + "pfc_asym": "off", "speed": "40000" }, "PORT|Ethernet116": { "admin_status": "up", "alias": "etp30", - "lanes": "116,117,118,119", + "description": "ARISTA02T1:PORT|Ethernet1", + "index": "29", + "lanes": "89,90,91,92", "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet120": { + "admin_status": "up", + "alias": "etp31", + "description": "ARISTA03T1:PORT|Ethernet1", + "index": "30", + "lanes": "101,102,103,104", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000" + }, + "PORT|Ethernet124": { + "admin_status": "up", + "alias": "etp32", + "description": "ARISTA04T1:PORT|Ethernet1", + "index": "31", + "lanes": "97,98,99,100", + "mtu": "9100", + "pfc_asym": "off", "speed": "40000" }, "VLAN_SUB_INTERFACE|Ethernet0.10": { "admin_status": "up" }, "ACL_RULE|DATAACL|DEFAULT_RULE": { - "PACKET_ACTION": "DROP", - "PRIORITY": "1" + "PACKET_ACTION": "DROP", + "PRIORITY": "1" }, "ACL_RULE|DATAACL|RULE_1": { - "PACKET_ACTION": "FORWARD", - "PRIORITY": "9999", - "SRC_IP": "10.0.0.2/32" + "PACKET_ACTION": "FORWARD", + "PRIORITY": "9999", + "SRC_IP": "10.0.0.2/32" }, "ACL_RULE|DATAACL|RULE_2": { - "DST_IP": "192.168.0.16/32", - "PACKET_ACTION": "FORWARD", - "PRIORITY": "9998" + "DST_IP": "192.168.0.16/32", + "PACKET_ACTION": "FORWARD", + "PRIORITY": "9998" }, "ACL_RULE|DATAACL|RULE_3": { - "DST_IP": "172.16.2.0/32", - "PACKET_ACTION": "FORWARD", - "PRIORITY": "9997" + "DST_IP": "172.16.2.0/32", + "PACKET_ACTION": "FORWARD", + "PRIORITY": "9997" }, "ACL_RULE|DATAACL|RULE_4": { - "L4_SRC_PORT": "4661", - "PACKET_ACTION": "FORWARD", - "PRIORITY": "9996" + "L4_SRC_PORT": "4661", + "PACKET_ACTION": "FORWARD", + "PRIORITY": "9996" }, "ACL_RULE|DATAACL|RULE_05": { - "IP_PROTOCOL": "126", - "PACKET_ACTION": "FORWARD", - "PRIORITY": "9995" + "IP_PROTOCOL": "126", + "PACKET_ACTION": "FORWARD", + "PRIORITY": "9995" }, "ACL_RULE|EVERFLOW|RULE_6": { - "PACKET_ACTION": "FORWARD", - "PRIORITY": "9994", - "TCP_FLAGS": "0x12/0x12" + "PACKET_ACTION": "FORWARD", + "PRIORITY": "9994", + "TCP_FLAGS": "0x12/0x12" }, "ACL_RULE|DATAACL|RULE_7": { - "PACKET_ACTION": "DROP", - "PRIORITY": "9993", - "SRC_IP": "10.0.0.3/32" + "PACKET_ACTION": "DROP", + "PRIORITY": "9993", + "SRC_IP": "10.0.0.3/32" }, "ACL_RULE|EVERFLOW|RULE_08": { - "PACKET_ACTION": "FORWARD", - "PRIORITY": "9992", - "SRC_IP": "10.0.0.3/32" + "PACKET_ACTION": "FORWARD", + "PRIORITY": "9992", + "SRC_IP": "10.0.0.3/32" }, "ACL_RULE|DATAACL|RULE_9": { - "L4_DST_PORT": "4661", - "PACKET_ACTION": "FORWARD", - "PRIORITY": "9991" + "L4_DST_PORT": "4661", + "PACKET_ACTION": "FORWARD", + "PRIORITY": "9991" }, "ACL_RULE|DATAACL|RULE_10": { - "PACKET_ACTION": "DROP", - "priority": "9989", - "SRC_IP": "10.0.0.3/32" + "PACKET_ACTION": "DROP", + "priority": "9989", + "SRC_IP": "10.0.0.3/32" }, - "ACL_TABLE|DATAACL": { - "policy_desc": "DATAACL", - "ports@": "PortChannel0002,PortChannel0005,PortChannel0008,PortChannel0011,PortChannel0014,PortChannel0017,PortChannel0020,PortChannel0023,Ethernet64,Ethernet68,Ethernet72,Ethernet76,Ethernet80,Ethernet84,Ethernet88,Ethernet92,Ethernet96,Ethernet100,Ethernet104,Ethernet108,Ethernet112,Ethernet116,Ethernet120,Ethernet124", - "type": "L3" + "ACL_TABLE|DATAACL": { + "policy_desc": "DATAACL", + "ports@": "PortChannel0002,PortChannel0005,PortChannel0008,PortChannel0011,PortChannel0014,PortChannel0017,PortChannel0020,PortChannel0023,Ethernet64,Ethernet68,Ethernet72,Ethernet76,Ethernet80,Ethernet84,Ethernet88,Ethernet92,Ethernet96,Ethernet100,Ethernet104,Ethernet108,Ethernet112,Ethernet116,Ethernet120,Ethernet124", + "type": "L3" }, "ACL_TABLE|EVERFLOW": { - "policy_desc": "EVERFLOW", - "ports@": "PortChannel0002,PortChannel0005,PortChannel0008,PortChannel0011,PortChannel0014,PortChannel0017,PortChannel0020,PortChannel0023,Ethernet100,Ethernet104,Ethernet92,Ethernet96,Ethernet84,Ethernet88,Ethernet76,Ethernet80,Ethernet108,Ethernet112,Ethernet64,Ethernet120,Ethernet116,Ethernet124,Ethernet72,Ethernet68", - "type": "MIRROR" + "policy_desc": "EVERFLOW", + "ports@": "PortChannel0002,PortChannel0005,PortChannel0008,PortChannel0011,PortChannel0014,PortChannel0017,PortChannel0020,PortChannel0023,Ethernet100,Ethernet104,Ethernet92,Ethernet96,Ethernet84,Ethernet88,Ethernet76,Ethernet80,Ethernet108,Ethernet112,Ethernet64,Ethernet120,Ethernet116,Ethernet124,Ethernet72,Ethernet68", + "type": "MIRROR" }, "ACL_TABLE|EVERFLOW_EGRESS": { - "policy_desc": "EGRESS EVERFLOW", - "ports@": "PortChannel0002,PortChannel0005,PortChannel0008,PortChannel0011,PortChannel0014,PortChannel0017,PortChannel0020,PortChannel0023,Ethernet100,Ethernet104,Ethernet92,Ethernet96,Ethernet84,Ethernet88,Ethernet76,Ethernet80,Ethernet108,Ethernet112,Ethernet64,Ethernet120,Ethernet116,Ethernet124,Ethernet72,Ethernet68", - "type": "MIRROR", - "stage": "egress" + "policy_desc": "EGRESS EVERFLOW", + "ports@": "PortChannel0002,PortChannel0005,PortChannel0008,PortChannel0011,PortChannel0014,PortChannel0017,PortChannel0020,PortChannel0023,Ethernet100,Ethernet104,Ethernet92,Ethernet96,Ethernet84,Ethernet88,Ethernet76,Ethernet80,Ethernet108,Ethernet112,Ethernet64,Ethernet120,Ethernet116,Ethernet124,Ethernet72,Ethernet68", + "type": "MIRROR", + "stage": "egress" }, "ACL_TABLE|SNMP_ACL": { - "policy_desc": "SNMP_ACL", - "services@": "SNMP", - "type": "CTRLPLANE" + "policy_desc": "SNMP_ACL", + "services@": "SNMP", + "type": "CTRLPLANE" }, "ACL_TABLE|SSH_ONLY": { - "policy_desc": "SSH_ONLY", - "services@": "SSH", - "type": "CTRLPLANE" - }, + "policy_desc": "SSH_ONLY", + "services@": "SSH", + "type": "CTRLPLANE" + }, + "VLAN|Vlan1000": { + "dhcp_servers@": "192.0.0.1,192.0.0.2,192.0.0.3,192.0.0.4", + "vlanid": "1000" + }, + "VLAN_INTERFACE|Vlan1000": { + "NULL": "NULL" + }, + "VLAN_INTERFACE|Vlan1000|192.168.0.1/21": { + "NULL": "NULL" + }, + "VLAN_INTERFACE|Vlan1000|fc02:1000::1/64": { + "NULL": "NULL" + }, + "VLAN_MEMBER|Vlan1000|Ethernet4": { + "tagging_mode": "untagged" + }, + "VLAN_MEMBER|Vlan1000|Ethernet8": { + "tagging_mode": "untagged" + }, + "VLAN_MEMBER|Vlan1000|Ethernet12": { + "tagging_mode": "untagged" + }, + "VLAN_MEMBER|Vlan1000|Ethernet16": { + "tagging_mode": "untagged" + }, + "PORTCHANNEL|PortChannel1001": { + "admin_status": "up", + "members@": "Ethernet32", + "min_links": "1", + "mtu": "9100" + }, + "PORTCHANNEL|PortChannel0001": { + "admin_status": "up", + "members@": "Ethernet112", + "min_links": "1", + "mtu": "9100" + }, + "PORTCHANNEL|PortChannel0002": { + "admin_status": "up", + "members@": "Ethernet116", + "min_links": "1", + "mtu": "9100" + }, + "PORTCHANNEL|PortChannel0003": { + "admin_status": "up", + "members@": "Ethernet120", + "min_links": "1", + "mtu": "9100" + }, + "PORTCHANNEL|PortChannel0004": { + "admin_status": "up", + "members@": "Ethernet124", + "min_links": "1", + "mtu": "9100" + }, + "PORTCHANNEL_MEMBER|PortChannel1001|Ethernet32": {"NULL": "NULL"}, + "PORTCHANNEL_MEMBER|PortChannel0001|Ethernet112": {"NULL": "NULL"}, + "PORTCHANNEL_MEMBER|PortChannel0002|Ethernet116": {"NULL": "NULL"}, + "PORTCHANNEL_MEMBER|PortChannel0003|Ethernet120": {"NULL": "NULL"}, + "PORTCHANNEL_MEMBER|PortChannel0004|Ethernet124": {"NULL": "NULL"}, + "PORTCHANNEL_INTERFACE|PortChannel0001": {"NULL": "NULL"}, + "PORTCHANNEL_INTERFACE|PortChannel0002": {"NULL": "NULL"}, + "PORTCHANNEL_INTERFACE|PortChannel0003": {"NULL": "NULL"}, + "PORTCHANNEL_INTERFACE|PortChannel0004": {"NULL": "NULL"}, + "PORTCHANNEL_INTERFACE|PortChannel0001|10.0.0.56/31": {"NULL": "NULL"}, + "PORTCHANNEL_INTERFACE|PortChannel0001|FC00::71/126": {"NULL": "NULL"}, + "PORTCHANNEL_INTERFACE|PortChannel0002|10.0.0.58/31": {"NULL": "NULL"}, + "PORTCHANNEL_INTERFACE|PortChannel0002|FC00::75/126": {"NULL": "NULL"}, + "PORTCHANNEL_INTERFACE|PortChannel0003|10.0.0.60/31": {"NULL": "NULL"}, + "PORTCHANNEL_INTERFACE|PortChannel0003|FC00::79/126": {"NULL": "NULL"}, + "PORTCHANNEL_INTERFACE|PortChannel0004|10.0.0.62/31": {"NULL": "NULL"}, + "PORTCHANNEL_INTERFACE|PortChannel0004|FC00::7D/126": {"NULL": "NULL"}, "DEBUG_COUNTER|DEBUG_0": { "type": "PORT_INGRESS_DROPS" }, diff --git a/tests/vlan_test.py b/tests/vlan_test.py new file mode 100644 index 0000000000..04aa16fa74 --- /dev/null +++ b/tests/vlan_test.py @@ -0,0 +1,461 @@ +import os +import traceback + +from click.testing import CliRunner + +import config.main as config +import show.main as show +from utilities_common.db import Db + +show_vlan_brief_output="""\ ++-----------+-----------------+------------+----------------+-----------------------+ +| VLAN ID | IP Address | Ports | Port Tagging | DHCP Helper Address | ++===========+=================+============+================+=======================+ +| 1000 | 192.168.0.1/21 | Ethernet4 | untagged | 192.0.0.1 | +| | fc02:1000::1/64 | Ethernet8 | untagged | 192.0.0.2 | +| | | Ethernet12 | untagged | 192.0.0.3 | +| | | Ethernet16 | untagged | 192.0.0.4 | ++-----------+-----------------+------------+----------------+-----------------------+ +""" + +show_vlan_brief_in_alias_mode_output="""\ ++-----------+-----------------+---------+----------------+-----------------------+ +| VLAN ID | IP Address | Ports | Port Tagging | DHCP Helper Address | ++===========+=================+=========+================+=======================+ +| 1000 | 192.168.0.1/21 | etp2 | untagged | 192.0.0.1 | +| | fc02:1000::1/64 | etp3 | untagged | 192.0.0.2 | +| | | etp4 | untagged | 192.0.0.3 | +| | | etp5 | untagged | 192.0.0.4 | ++-----------+-----------------+---------+----------------+-----------------------+ +""" + +show_vlan_brief_empty_output="""\ ++-----------+--------------+---------+----------------+-----------------------+ +| VLAN ID | IP Address | Ports | Port Tagging | DHCP Helper Address | ++===========+==============+=========+================+=======================+ ++-----------+--------------+---------+----------------+-----------------------+ +""" + +show_vlan_brief_with_portchannel_output="""\ ++-----------+-----------------+-----------------+----------------+-----------------------+ +| VLAN ID | IP Address | Ports | Port Tagging | DHCP Helper Address | ++===========+=================+=================+================+=======================+ +| 1000 | 192.168.0.1/21 | Ethernet4 | untagged | 192.0.0.1 | +| | fc02:1000::1/64 | Ethernet8 | untagged | 192.0.0.2 | +| | | Ethernet12 | untagged | 192.0.0.3 | +| | | Ethernet16 | untagged | 192.0.0.4 | +| | | PortChannel1001 | untagged | | ++-----------+-----------------+-----------------+----------------+-----------------------+ +""" + +show_vlan_config_output="""\ +Name VID +-------- ----- +Vlan1000 1000 +""" + +config_vlan_add_dhcp_relay_output="""\ +Added DHCP relay destination address 192.0.0.100 to Vlan1000 +Restarting DHCP relay service... +""" + +config_vlan_del_dhcp_relay_output="""\ +Removed DHCP relay destination address 192.0.0.100 from Vlan1000 +Restarting DHCP relay service... +""" + +show_vlan_brief_output_with_new_dhcp_relay_address="""\ ++-----------+-----------------+------------+----------------+-----------------------+ +| VLAN ID | IP Address | Ports | Port Tagging | DHCP Helper Address | ++===========+=================+============+================+=======================+ +| 1000 | 192.168.0.1/21 | Ethernet4 | untagged | 192.0.0.1 | +| | fc02:1000::1/64 | Ethernet8 | untagged | 192.0.0.2 | +| | | Ethernet12 | untagged | 192.0.0.3 | +| | | Ethernet16 | untagged | 192.0.0.4 | +| | | | | 192.0.0.100 | ++-----------+-----------------+------------+----------------+-----------------------+ +""" + +config_add_del_vlan_and_vlan_member_output="""\ ++-----------+-----------------+------------+----------------+-----------------------+ +| VLAN ID | IP Address | Ports | Port Tagging | DHCP Helper Address | ++===========+=================+============+================+=======================+ +| 1000 | 192.168.0.1/21 | Ethernet4 | untagged | 192.0.0.1 | +| | fc02:1000::1/64 | Ethernet8 | untagged | 192.0.0.2 | +| | | Ethernet12 | untagged | 192.0.0.3 | +| | | Ethernet16 | untagged | 192.0.0.4 | ++-----------+-----------------+------------+----------------+-----------------------+ +| 1001 | | Ethernet20 | untagged | | ++-----------+-----------------+------------+----------------+-----------------------+ +""" + +config_add_del_vlan_and_vlan_member_in_alias_mode_output="""\ ++-----------+-----------------+---------+----------------+-----------------------+ +| VLAN ID | IP Address | Ports | Port Tagging | DHCP Helper Address | ++===========+=================+=========+================+=======================+ +| 1000 | 192.168.0.1/21 | etp2 | untagged | 192.0.0.1 | +| | fc02:1000::1/64 | etp3 | untagged | 192.0.0.2 | +| | | etp4 | untagged | 192.0.0.3 | +| | | etp5 | untagged | 192.0.0.4 | ++-----------+-----------------+---------+----------------+-----------------------+ +| 1001 | | etp6 | untagged | | ++-----------+-----------------+---------+----------------+-----------------------+ +""" +class TestVlan(object): + @classmethod + def setup_class(cls): + os.environ['UTILITIES_UNIT_TESTING'] = "1" + print("SETUP") + + def test_show_vlan(self): + runner = CliRunner() + result = runner.invoke(show.cli.commands["vlan"], []) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + + def test_show_vlan_brief(self): + runner = CliRunner() + result = runner.invoke(show.cli.commands["vlan"].commands["brief"], []) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_vlan_brief_output + + def test_show_vlan_brief_verbose(self): + runner = CliRunner() + result = runner.invoke(show.cli.commands["vlan"].commands["brief"], ["--verbose"]) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_vlan_brief_output + + def test_show_vlan_brief_in_alias_mode(self): + runner = CliRunner() + os.environ['SONIC_CLI_IFACE_MODE'] = "alias" + result = runner.invoke(show.cli.commands["vlan"].commands["brief"]) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_vlan_brief_in_alias_mode_output + os.environ['SONIC_CLI_IFACE_MODE'] = "" + + def test_show_vlan_config(self): + runner = CliRunner() + result = runner.invoke(show.cli.commands["vlan"].commands["config"], []) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_vlan_config_output + + def test_config_vlan_add_vlan_with_invalid_vlanid(self): + runner = CliRunner() + result = runner.invoke(config.config.commands["vlan"].commands["add"], ["4096"]) + print(result.exit_code) + print(result.output) + assert result.exit_code != 0 + assert "Error: Invalid VLAN ID 4096 (1-4094)" in result.output + + def test_config_vlan_add_vlan_with_exist_vlanid(self): + runner = CliRunner() + result = runner.invoke(config.config.commands["vlan"].commands["add"], ["1000"]) + print(result.exit_code) + print(result.output) + assert result.exit_code != 0 + assert "Error: Vlan1000 already exists" in result.output + + def test_config_vlan_del_vlan_with_invalid_vlanid(self): + runner = CliRunner() + result = runner.invoke(config.config.commands["vlan"].commands["del"], ["4096"]) + print(result.exit_code) + print(result.output) + assert result.exit_code != 0 + assert "Error: Invalid VLAN ID 4096 (1-4094)" in result.output + + def test_config_vlan_del_vlan_with_nonexist_vlanid(self): + runner = CliRunner() + result = runner.invoke(config.config.commands["vlan"].commands["del"], ["1001"]) + print(result.exit_code) + print(result.output) + assert result.exit_code != 0 + assert "Error: Vlan1001 does not exist" in result.output + + def test_config_vlan_add_member_with_invalid_vlanid(self): + runner = CliRunner() + result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], ["4096", "Ethernet4"]) + print(result.exit_code) + print(result.output) + assert result.exit_code != 0 + assert "Error: Invalid VLAN ID 4096 (1-4094)" in result.output + + def test_config_vlan_add_member_with_nonexist_vlanid(self): + runner = CliRunner() + result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], ["1001", "Ethernet4"]) + print(result.exit_code) + print(result.output) + assert result.exit_code != 0 + assert "Error: Vlan1001 does not exist" in result.output + + def test_config_vlan_add_exist_port_member(self): + runner = CliRunner() + result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], ["1000", "Ethernet4"]) + print(result.exit_code) + print(result.output) + assert result.exit_code != 0 + assert "Error: Ethernet4 is already a member of Vlan1000" in result.output + + def test_config_vlan_add_nonexist_port_member(self): + runner = CliRunner() + result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], ["1000", "Ethernet3"]) + print(result.exit_code) + print(result.output) + assert result.exit_code != 0 + assert "Error: Ethernet3 does not exist" in result.output + + def test_config_vlan_add_nonexist_portchannel_member(self): + runner = CliRunner() + result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], \ + ["1000", "PortChannel1011"]) + print(result.exit_code) + print(result.output) + assert result.exit_code != 0 + assert "Error: PortChannel1011 does not exist" in result.output + + def test_config_vlan_add_portchannel_member(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], \ + ["1000", "PortChannel1001", "--untagged"], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + + # show output + result = runner.invoke(show.cli.commands["vlan"].commands["brief"], [], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_vlan_brief_with_portchannel_output + + def test_config_vlan_add_rif_portchannel_member(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], \ + ["1000", "PortChannel0001", "--untagged"], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code != 0 + assert "Error: PortChannel0001 is a router interface!" in result.output + + def test_config_vlan_del_vlan(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(config.config.commands["vlan"].commands["del"], ["1000"], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + + # show output + result = runner.invoke(show.cli.commands["vlan"].commands["brief"], [], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_vlan_brief_empty_output + + def test_config_vlan_del_nonexist_vlan_member(self): + runner = CliRunner() + + result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["del"], \ + ["1000", "Ethernet0"]) + print(result.exit_code) + print(result.output) + assert result.exit_code != 0 + assert "Error: Ethernet0 is not a member of Vlan1000" in result.output + + def test_config_add_del_vlan_and_vlan_member(self): + runner = CliRunner() + db = Db() + + # add vlan 1001 + result = runner.invoke(config.config.commands["vlan"].commands["add"], ["1001"], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + + # add Ethernet20 to vlan 1001 + result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], + ["1001", "Ethernet20", "--untagged"], obj=db) + print(result.exit_code) + print(result.output) + traceback.print_tb(result.exc_info[2]) + assert result.exit_code == 0 + + # show output + result = runner.invoke(show.cli.commands["vlan"].commands["brief"], [], obj=db) + print(result.output) + assert result.output == config_add_del_vlan_and_vlan_member_output + + # remove vlan member + result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["del"], + ["1001", "Ethernet20"], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + + # add del 1001 + result = runner.invoke(config.config.commands["vlan"].commands["del"], ["1001"], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + + # show output + result = runner.invoke(show.cli.commands["vlan"].commands["brief"], [], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_vlan_brief_output + + def test_config_add_del_vlan_and_vlan_member_in_alias_mode(self): + runner = CliRunner() + db = Db() + + os.environ['SONIC_CLI_IFACE_MODE'] = "alias" + + # add vlan 1001 + result = runner.invoke(config.config.commands["vlan"].commands["add"], ["1001"], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + + # add etp6 to vlan 1001 + result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], + ["1001", "etp6", "--untagged"], obj=db) + print(result.exit_code) + print(result.output) + traceback.print_tb(result.exc_info[2]) + assert result.exit_code == 0 + + # show output + result = runner.invoke(show.cli.commands["vlan"].commands["brief"], [], obj=db) + print(result.output) + assert result.output == config_add_del_vlan_and_vlan_member_in_alias_mode_output + + # remove vlan member + result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["del"], + ["1001", "etp6"], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + + # add del 1001 + result = runner.invoke(config.config.commands["vlan"].commands["del"], ["1001"], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + + # show output + result = runner.invoke(show.cli.commands["vlan"].commands["brief"], [], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_vlan_brief_in_alias_mode_output + + os.environ['SONIC_CLI_IFACE_MODE'] = "" + + def test_config_vlan_add_dhcp_relay_with_nonexist_vlanid(self): + runner = CliRunner() + result = runner.invoke(config.config.commands["vlan"].commands["dhcp_relay"].commands["add"], + ["1001", "192.0.0.100"]) + print(result.exit_code) + print(result.output) + # traceback.print_tb(result.exc_info[2]) + assert result.exit_code != 0 + assert "Error: Vlan1001 doesn't exist" in result.output + + def test_config_vlan_add_dhcp_relay_with_invalid_vlanid(self): + runner = CliRunner() + result = runner.invoke(config.config.commands["vlan"].commands["dhcp_relay"].commands["add"], + ["4096", "192.0.0.100"]) + print(result.exit_code) + print(result.output) + # traceback.print_tb(result.exc_info[2]) + assert result.exit_code != 0 + assert "Error: Vlan4096 doesn't exist" in result.output + + def test_config_vlan_add_dhcp_relay_with_invalid_ip(self): + runner = CliRunner() + result = runner.invoke(config.config.commands["vlan"].commands["dhcp_relay"].commands["add"], + ["1000", "192.0.0.1000"]) + print(result.exit_code) + print(result.output) + # traceback.print_tb(result.exc_info[2]) + assert result.exit_code != 0 + assert "Error: 192.0.0.1000 is invalid IP address" in result.output + + def test_config_vlan_add_dhcp_relay_with_exist_ip(self): + runner = CliRunner() + result = runner.invoke(config.config.commands["vlan"].commands["dhcp_relay"].commands["add"], + ["1000", "192.0.0.1"]) + print(result.exit_code) + print(result.output) + # traceback.print_tb(result.exc_info[2]) + assert result.exit_code == 0 + assert "192.0.0.1 is already a DHCP relay destination for Vlan1000" in result.output + + def test_config_vlan_add_del_dhcp_relay_dest(self): + runner = CliRunner() + db = Db() + + # add new relay dest + result = runner.invoke(config.config.commands["vlan"].commands["dhcp_relay"].commands["add"], + ["1000", "192.0.0.100"], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == config_vlan_add_dhcp_relay_output + + # show output + result = runner.invoke(show.cli.commands["vlan"].commands["brief"], [], obj=db) + print(result.output) + assert result.output == show_vlan_brief_output_with_new_dhcp_relay_address + + # del relay dest + result = runner.invoke(config.config.commands["vlan"].commands["dhcp_relay"].commands["del"], + ["1000", "192.0.0.100"], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == config_vlan_del_dhcp_relay_output + + # show output + result = runner.invoke(show.cli.commands["vlan"].commands["brief"], [], obj=db) + print(result.output) + assert result.output == show_vlan_brief_output + + def test_config_vlan_remove_nonexist_dhcp_relay_dest(self): + runner = CliRunner() + result = runner.invoke(config.config.commands["vlan"].commands["dhcp_relay"].commands["del"], + ["1000", "192.0.0.100"]) + print(result.exit_code) + print(result.output) + # traceback.print_tb(result.exc_info[2]) + assert result.exit_code != 0 + assert "Error: 192.0.0.100 is not a DHCP relay destination for Vlan1000" in result.output + + def test_config_vlan_remove_dhcp_relay_dest_with_nonexist_vlanid(self): + runner = CliRunner() + result = runner.invoke(config.config.commands["vlan"].commands["dhcp_relay"].commands["del"], + ["1001", "192.0.0.1"]) + print(result.exit_code) + print(result.output) + # traceback.print_tb(result.exc_info[2]) + assert result.exit_code != 0 + assert "Error: Vlan1001 doesn't exist" in result.output + + @classmethod + def teardown_class(cls): + os.environ['UTILITIES_UNIT_TESTING'] = "0" + print("TEARDOWN") diff --git a/utilities_common/cli.py b/utilities_common/cli.py index dd09a444c5..bbbb6df471 100644 --- a/utilities_common/cli.py +++ b/utilities_common/cli.py @@ -1,7 +1,16 @@ +import os +import sys +import netaddr +import subprocess + import click from utilities_common.db import Db +from swsssdk import ConfigDBConnector + +VLAN_SUB_INTERFACE_SEPARATOR = '.' + pass_db = click.make_pass_decorator(Db, ensure=True) class AbbreviationGroup(click.Group): @@ -41,3 +50,251 @@ def get_command(self, ctx, cmd_name): return click.Group.get_command(self, ctx, shortest) ctx.fail('Too many matches: %s' % ', '.join(sorted(matches))) + +try: + import ConfigParser as configparser +except ImportError: + import configparser + +# This is from the aliases example: +# https://github.com/pallets/click/blob/57c6f09611fc47ca80db0bd010f05998b3c0aa95/examples/aliases/aliases.py +class Config(object): + """Object to hold CLI config""" + + def __init__(self): + self.path = os.getcwd() + self.aliases = {} + + def read_config(self, filename): + parser = configparser.RawConfigParser() + parser.read([filename]) + try: + self.aliases.update(parser.items('aliases')) + except configparser.NoSectionError: + pass + +# Global Config object +_config = None + +class AliasedGroup(click.Group): + """This subclass of click.Group supports abbreviations and + looking up aliases in a config file with a bit of magic. + """ + + def get_command(self, ctx, cmd_name): + global _config + + # If we haven't instantiated our global config, do it now and load current config + if _config is None: + _config = Config() + + # Load our config file + cfg_file = os.path.join(os.path.dirname(__file__), 'aliases.ini') + _config.read_config(cfg_file) + + # Try to get builtin commands as normal + rv = click.Group.get_command(self, ctx, cmd_name) + if rv is not None: + return rv + + # No builtin found. Look up an explicit command alias in the config + if cmd_name in _config.aliases: + actual_cmd = _config.aliases[cmd_name] + return click.Group.get_command(self, ctx, actual_cmd) + + # Alternative option: if we did not find an explicit alias we + # allow automatic abbreviation of the command. "status" for + # instance will match "st". We only allow that however if + # there is only one command. + matches = [x for x in self.list_commands(ctx) + if x.lower().startswith(cmd_name.lower())] + if not matches: + return None + elif len(matches) == 1: + return click.Group.get_command(self, ctx, matches[0]) + ctx.fail('Too many matches: %s' % ', '.join(sorted(matches))) + +class InterfaceAliasConverter(object): + """Class which handles conversion between interface name and alias""" + + def __init__(self, db=None): + + if db is None: + self.config_db = ConfigDBConnector() + self.config_db.connect() + else: + self.config_db = db.cfgdb + + self.alias_max_length = 0 + self.port_dict = self.config_db.get_table('PORT') + + if not self.port_dict: + click.echo(message="Warning: failed to retrieve PORT table from ConfigDB!", err=True) + self.port_dict = {} + + for port_name in self.port_dict.keys(): + try: + if self.alias_max_length < len( + self.port_dict[port_name]['alias']): + self.alias_max_length = len( + self.port_dict[port_name]['alias']) + except KeyError: + break + + def name_to_alias(self, interface_name): + """Return vendor interface alias if SONiC + interface name is given as argument + """ + vlan_id = '' + sub_intf_sep_idx = -1 + if interface_name is not None: + sub_intf_sep_idx = interface_name.find(VLAN_SUB_INTERFACE_SEPARATOR) + if sub_intf_sep_idx != -1: + vlan_id = interface_name[sub_intf_sep_idx + 1:] + # interface_name holds the parent port name + interface_name = interface_name[:sub_intf_sep_idx] + + for port_name in self.port_dict.keys(): + if interface_name == port_name: + return self.port_dict[port_name]['alias'] if sub_intf_sep_idx == -1 \ + else self.port_dict[port_name]['alias'] + VLAN_SUB_INTERFACE_SEPARATOR + vlan_id + + # interface_name not in port_dict. Just return interface_name + return interface_name if sub_intf_sep_idx == -1 else interface_name + VLAN_SUB_INTERFACE_SEPARATOR + vlan_id + + def alias_to_name(self, interface_alias): + """Return SONiC interface name if vendor + port alias is given as argument + """ + vlan_id = '' + sub_intf_sep_idx = -1 + if interface_alias is not None: + sub_intf_sep_idx = interface_alias.find(VLAN_SUB_INTERFACE_SEPARATOR) + if sub_intf_sep_idx != -1: + vlan_id = interface_alias[sub_intf_sep_idx + 1:] + # interface_alias holds the parent port alias + interface_alias = interface_alias[:sub_intf_sep_idx] + + for port_name in self.port_dict.keys(): + if interface_alias == self.port_dict[port_name]['alias']: + return port_name if sub_intf_sep_idx == -1 else port_name + VLAN_SUB_INTERFACE_SEPARATOR + vlan_id + + # interface_alias not in port_dict. Just return interface_alias + return interface_alias if sub_intf_sep_idx == -1 else interface_alias + VLAN_SUB_INTERFACE_SEPARATOR + vlan_id + +def get_interface_naming_mode(): + mode = os.getenv('SONIC_CLI_IFACE_MODE') + if mode is None: + mode = "default" + return mode + +def is_ipaddress(val): + """ Validate if an entry is a valid IP """ + if not val: + return False + try: + netaddr.IPAddress(str(val)) + except netaddr.core.AddrFormatError: + return False + return True + + +def is_ip_prefix_in_key(key): + ''' + Function to check if IP address is present in the key. If it + is present, then the key would be a tuple or else, it shall be + be string + ''' + return (isinstance(key, tuple)) + +def is_valid_port(config_db, port): + """Check if port is in PORT table""" + + port_table = config_db.get_table('PORT') + if port in port_table.keys(): + return True + + return False + +def is_valid_portchannel(config_db, port): + """Check if port is in PORT_CHANNEL table""" + + pc_table = config_db.get_table('PORTCHANNEL') + if port in pc_table.keys(): + return True + + return False + +def is_vlanid_in_range(vid): + """Check if vlan id is valid or not""" + + if vid >= 1 and vid <= 4094: + return True + + return False + +def check_if_vlanid_exist(config_db, vlan): + """Check if vlan id exits in the config db or ot""" + + if len(config_db.get_entry('VLAN', vlan)) != 0: + return True + + return False + +def is_port_vlan_member(config_db, port, vlan): + """Check if port is a member of vlan""" + + vlan_ports_data = config_db.get_table('VLAN_MEMBER') + for key in vlan_ports_data.keys(): + if key[0] == vlan and key[1] == port: + return True + + return False + +def is_port_router_interface(config_db, port): + """Check if port is a router interface""" + + interface_table = config_db.get_table('INTERFACE') + for intf in interface_table.keys(): + if port == intf[0]: + return True + + return False + +def is_pc_router_interface(config_db, pc): + """Check if portchannel is a router interface""" + + pc_interface_table = config_db.get_table('PORTCHANNEL_INTERFACE') + for intf in pc_interface_table.keys(): + if pc == intf[0]: + return True + + return False + +def is_port_mirror_dst_port(config_db, port): + """ Check if port is already configured as mirror destination port """ + mirror_table = config_db.get_table('MIRROR_SESSION') + for _,v in mirror_table.items(): + if 'dst_port' in v and v['dst_port'] == port: + return True + + return False + +def run_command(command, display_cmd=False, ignore_error=False): + """Run bash command and print output to stdout + """ + + if display_cmd == True: + click.echo(click.style("Running command: ", fg='cyan') + click.style(command, fg='green')) + + if os.environ["UTILITIES_UNIT_TESTING"] == "1": + return + + proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) + (out, err) = proc.communicate() + + if len(out) > 0: + click.echo(out) + + if proc.returncode != 0 and not ignore_error: + sys.exit(proc.returncode)