From 89132515d872b027636ece6e924bfd3e06720574 Mon Sep 17 00:00:00 2001 From: Garrick He Date: Tue, 11 Jun 2019 15:19:24 -0700 Subject: [PATCH 01/15] sonic-utilities: Add sFlow CLIs * Add configuration CLI to add/delete collector * Add configuration CLI to set sampling-rate * Add show CLI to display sFlow information Signed-off-by: Garrick He --- config/main.py | 167 +++++++++++++++++++++++++++++++++++++++++++++++++ show/main.py | 38 ++++++++++- 2 files changed, 202 insertions(+), 3 deletions(-) diff --git a/config/main.py b/config/main.py index ebc625cadb..44fb2da692 100755 --- a/config/main.py +++ b/config/main.py @@ -1229,5 +1229,172 @@ def naming_mode_alias(): set_interface_naming_mode('alias') +# +# 'sflow' group ('config sflow ...') +# +@config.group() +def sflow(): + """sFlow-related configuration tasks""" + pass + + +# +# 'sflow' command ('config sflow samplerate ...') +# +@sflow.command() +@click.argument('rate', required=True, type=float) +def samplerate(rate): + """Set the sFlow sample rate.""" + if (rate > 1): + click.echo("Error: Sample rate must be less than 1") + return + + config_db = ConfigDBConnector() + config_db.connect() + + # Currently only supporting global sample-rate instead of + # per port + rate_dict = config_db.get_table('SFLOW_CONFIG') + if rate_dict: + config_db.mod_entry('SFLOW_CONFIG', 'global', {'key' : 'global', 'sample_rate' : rate}) + else: + config_db.set_entry('SFLOW_CONFIG', 'global', {'key' : 'global', 'sample_rate' : rate}) + + +# +# 'sflow genetlink' group +# +@sflow.group() +def genetlink(): + """Update sFlow Netlink groups""" + pass + + +def is_valid_genetlink_input(name, group_id): + if len(name) > 16: + click.echo("Netlink family name must be 16 characters or less") + return false + if group_id > 9999: + click.echo("Netlink multicast ID must be 4 digits or less") + return false + return true + + +# +# 'sflow' command ('config sflow genetlink add ...') +# +@genetlink.command() +@click.argument('name', required=True, type=str) +@click.argument('group_id', required=True, type=int) +def add(name, group_id): + """Add a new Netlink group""" + if not is_valid_genetlink_input(name, group_id): + return + + config_db = ConfigDBConnector() + config_db.connect() + config_db.set_entry('SFLOW_GENETLINK', name, {'genetlink_name' : name, 'genetlink_id' : group_id}) + + +# +# 'sflow' command ('config sflow genetlink del ...') +# +@genetlink.command('del') +@click.argument('name', required=True, type=str) +@click.argument('group_id', required=True, type=int) +def clear(name, group_id): + """Delete a Netlink group""" + if not valid_genetlink_input(name, group_id): + return + + config_db = ConfigDBConnector() + config_db.connect() + config_db.set_entry('SFLOW_GENETLINK', name, None) + + +# +# 'sflow collector' group +# +@sflow.group() +def collector(): + """Add/Delete a sFlow collector""" + pass + + +# +# 'sflow' command ('config sflow collector del ...') +# +@collector.command('del') +@click.argument('name', required=True) +def del_collector(name): + """Delete a sFlow collector""" + config_db = ConfigDBConnector() + config_db.connect() + config_db.set_entry('SFLOW_COLLECTOR', name, None) + + +def is_valid_collector_info(name, ip, agentaddr, port, maxsize): + if len(name) > 250: # TODO get max length for collector name + click.echo("Collector name too long") + return False + if port > 99999: + click.echo("Collector port number must be 5-digits or less") + return False + if maxsize > 9999: + click.echo("Maximum datagram size must be 4-digits or less") + return False + + import socket + valid_input = True + try: + socket.inet_pton(socket.AF_INET, ip) + except: + try: + socket.inet_pton(socket.AF_INET6, ip) + except: + valid_input = False + click.echo("Invalid collector IPv4 or IPv6 address") + + if agentaddr: + try: + socket.inet_pton(socket.AF_INET, agentaddr) + except: + try: + socket.inet_pton(socket.AF_INET6, agentaddr) + except: + valid_input = False + click.echo("Invalid agent IPv4 or IPv6 address") + + return valid_input + +def make_collector_info_dict(ip, agentaddr, port, maxsize): + return {"collector_ip" : ip, "agent_addr" : agentaddr, "collector_port": port, \ + "max_datagram_size" : maxsize} + + +# +# 'sflow' command ('config sflow collector add ...') +# +@collector.command() +@click.option('--agentaddr', required=True, type=str, default=None, help="Agent IP address") +@click.option('--port', required=False, type=int, default=None, help="Collector port number") +@click.option('--maxsize', required=False, type=int, default=None, help="Maximum datagram size") +@click.argument('name', required=True) +@click.argument('ip', required=True) + +def add(name, ip, agentaddr, port, maxsize): + """Add a sFlow collector""" + ip = ip.lower() + if (agentaddr): + agentaddr = agentaddr.lower() + if not is_valid_collector_info(name, ip , agentaddr, port, maxsize): + return + + config_db = ConfigDBConnector() + config_db.connect() + config_db.set_entry('SFLOW_COLLECTOR', name, make_collector_info_dict(ip, agentaddr, port, maxsize)) + return + + if __name__ == '__main__': config() diff --git a/show/main.py b/show/main.py index b55184fe9e..d217cd598c 100755 --- a/show/main.py +++ b/show/main.py @@ -1118,7 +1118,7 @@ def table(verbose): def get_hw_info_dict(): """ - This function is used to get the HW info helper function + This function is used to get the HW info helper function """ hw_info_dict = {} machine_info = sonic_device_util.get_machine_info() @@ -1126,7 +1126,7 @@ def get_hw_info_dict(): config_db = ConfigDBConnector() config_db.connect() data = config_db.get_table('DEVICE_METADATA') - try: + try: hwsku = data['localhost']['hwsku'] except KeyError: hwsku = "Unknown" @@ -1216,7 +1216,7 @@ def version(verbose): version_info = sonic_device_util.get_sonic_version_info() hw_info_dict = get_hw_info_dict() serial_number_cmd = "sudo decode-syseeprom -s" - serial_number = subprocess.Popen(serial_number_cmd, shell=True, stdout=subprocess.PIPE) + serial_number = subprocess.Popen(serial_number_cmd, shell=True, stdout=subprocess.PIPE) sys_uptime_cmd = "uptime" sys_uptime = subprocess.Popen(sys_uptime_cmd, shell=True, stdout=subprocess.PIPE) click.echo("\nSONiC Software Version: SONiC.{}".format(version_info['build_version'])) @@ -1375,6 +1375,17 @@ def ntp(verbose): run_command(cmd, display_cmd=verbose) +# 'sflow' subcommand ("show runningconfiguration sflow") +@runningconfiguration.command() +@click.option('--verbose', is_flag=True, help="Enable verbose output") +def sflow(verbose): + """Show sFlow running configuration""" + cmd = "cat /etc/sflow.conf" + run_command(cmd, display_cmd=verbose) + cmd = "cat /etc/hsflow.conf" + run_command(cmd, display_cmd=verbose) + + # # 'startupconfiguration' group ("show startupconfiguration ...") # @@ -1675,6 +1686,27 @@ def policer(policer_name, verbose): run_command(cmd, display_cmd=verbose) +# +# 'sFlow' command ("show sflow") +# +@cli.command() +def sFlow(): + """Show sFlow related information""" + config_db = ConfigDBConnector() + config_db.connect() + sflow_info = config_db.get_table('SFLOW_CONFIG') + if sflow_info: + click.echo("\n\tSample-rate(s):") + for key in sflow_info: + click.echo("\t\tName: {}\t\t\tSample-rate: {}".format(key, sflow_info[key]['sample_rate'])) + sflow_info = config_db.get_table('SFLOW_COLLECTOR') + if sflow_info: + click.echo("\n\tsFlow Collector(s) information:") + for key in sorted(sflow_info): + click.echo("\t\tName: {}".format(key)) + for info_key in sflow_info[key]: + click.echo("\t\t\t{}: \t{}".format(info_key, sflow_info[key][info_key])) + # # 'acl' group ### From 6ba881fce85246ba0c27600b6e885ba9c6130389 Mon Sep 17 00:00:00 2001 From: Garrick He Date: Tue, 16 Jul 2019 15:33:37 -0700 Subject: [PATCH 02/15] Update sFlow CLI * Updated config/show commands to conform with HDL * Updated with code-review suggestions Signed-off-by: Garrick He --- config/main.py | 227 +++++++++++++++++++++++++++++++++---------------- show/main.py | 106 +++++++++++++++++------ 2 files changed, 232 insertions(+), 101 deletions(-) diff --git a/config/main.py b/config/main.py index 44fb2da692..aca6b52195 100755 --- a/config/main.py +++ b/config/main.py @@ -8,6 +8,7 @@ import netaddr import re import syslog +import netifaces import sonic_device_util import ipaddress @@ -1236,81 +1237,131 @@ def naming_mode_alias(): def sflow(): """sFlow-related configuration tasks""" pass - +# +# 'sflow' command ('config sflow enable ...') +# +@sflow.command() +def enable(): + """Enable sFlow""" + config_db = ConfigDBConnector() + config_db.connect() + sflow_tbl = config_db.get_table('SFLOW') + if not sflow_tbl: + sflow_tbl = {'global' : {'admin_state' : 'enable'}} + else: + sflow_tbl['global']['admin_state'] = 'enable' + config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) # -# 'sflow' command ('config sflow samplerate ...') +# 'sflow' command ('config sflow disable ...') # @sflow.command() -@click.argument('rate', required=True, type=float) -def samplerate(rate): - """Set the sFlow sample rate.""" - if (rate > 1): - click.echo("Error: Sample rate must be less than 1") - return +def disable(): + """Disable sFlow""" + config_db = ConfigDBConnector() + config_db.connect() + sflow_tbl = config_db.get_table('SFLOW') + if not sflow_tbl: + sflow_tbl = {'global' : {'admin_state' : 'disable'}} + else: + sflow_tbl['global']['admin_state'] = 'disable' + config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) +# +# 'sflow' command ('config sflow disable ...') +# +@sflow.command('polling-interval') +@click.argument('intv', required=True, type=int) +def polling_int(intv): + """Set polling-interval for counter-sampling (0 to disable)""" + if (0 > intv or intv > 300): + click.echo("Polling interval must be between 0-300") config_db = ConfigDBConnector() config_db.connect() + sflow_tbl = config_db.get_table('SFLOW') + if not sflow_tbl: + click.echo("sFlow not configured") + return + sflow_tbl['global']['polling_interval'] = intv + config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) - # Currently only supporting global sample-rate instead of - # per port - rate_dict = config_db.get_table('SFLOW_CONFIG') - if rate_dict: - config_db.mod_entry('SFLOW_CONFIG', 'global', {'key' : 'global', 'sample_rate' : rate}) - else: - config_db.set_entry('SFLOW_CONFIG', 'global', {'key' : 'global', 'sample_rate' : rate}) +def is_valid_sample_rate(rate): + return rate >= 256 and rate <= 8388608 +def valid_intf(intf): + interfaces = netifaces.interfaces() + return intf in interfaces # -# 'sflow genetlink' group +# 'sflow interface' group # @sflow.group() -def genetlink(): - """Update sFlow Netlink groups""" +def interface(): + """Configure sFlow on interface""" pass +# +# 'sflow' command ('config sflow interface enable ...') +# +@interface.command() +@click.argument('name', required=True, type=str) +def enable(name): + if not valid_intf(name): + click.echo("Invalid interface name") + return + config_db = ConfigDBConnector() + config_db.connect() -def is_valid_genetlink_input(name, group_id): - if len(name) > 16: - click.echo("Netlink family name must be 16 characters or less") - return false - if group_id > 9999: - click.echo("Netlink multicast ID must be 4 digits or less") - return false - return true + intf_dict = config_db.get_table('SFLOW_SESSION') + if intf_dict and name in intf_dict.keys(): + intf_dict[name]['admin_state'] = 'enable' + config_db.set_entry('SFLOW_SESSION', name, intf_dict[name]) + else: + config_db.set_entry('SFLOW_SESSION', name, {'admin_state' : 'enable'}) # -# 'sflow' command ('config sflow genetlink add ...') +# 'sflow' command ('config sflow interface disable ...') # -@genetlink.command() +@interface.command() @click.argument('name', required=True, type=str) -@click.argument('group_id', required=True, type=int) -def add(name, group_id): - """Add a new Netlink group""" - if not is_valid_genetlink_input(name, group_id): +def disable(name): + if not valid_intf(name): + click.echo("Invalid interface name") return - config_db = ConfigDBConnector() config_db.connect() - config_db.set_entry('SFLOW_GENETLINK', name, {'genetlink_name' : name, 'genetlink_id' : group_id}) + intf_dict = config_db.get_table('SFLOW_SESSION') + + if intf_dict and name in intf_dict.keys(): + intf_dict[name]['admin_state'] = 'disable' + config_db.set_entry('SFLOW_SESSION', name, intf_dict[name]) + else: + config_db.set_entry('SFLOW_SESSION', name, {'admin_state' : 'disable'}) # -# 'sflow' command ('config sflow genetlink del ...') +# 'sflow' command ('config sflow interface sample-rate ...') # -@genetlink.command('del') +@interface.command('sample-rate') @click.argument('name', required=True, type=str) -@click.argument('group_id', required=True, type=int) -def clear(name, group_id): - """Delete a Netlink group""" - if not valid_genetlink_input(name, group_id): +@click.argument('rate', required=True, type=int) +def sample_rate(name, rate): + if not valid_intf(name): + click.echo('Invalid interface name') + return + if not is_valid_sample_rate(rate): + click.echo('Error: Sample rate must be between 256 and 8388608') return config_db = ConfigDBConnector() config_db.connect() - config_db.set_entry('SFLOW_GENETLINK', name, None) - + sess_dict = config_db.get_table('SFLOW_SESSION') + if sess_dict and name in sess_dict.keys(): + sess_dict[name]['sample_rate'] = rate + config_db.set_entry('SFLOW_SESSION', name, sess_dict[name]) + elif sess_dict: + config_db.set_entry('SFLOW_SESSION', name, {'sample_rate' : rate}) # # 'sflow collector' group @@ -1333,68 +1384,96 @@ def del_collector(name): config_db.set_entry('SFLOW_COLLECTOR', name, None) -def is_valid_collector_info(name, ip, agentaddr, port, maxsize): - if len(name) > 250: # TODO get max length for collector name - click.echo("Collector name too long") +def is_valid_collector_info(name, ip, port): + if len(name) > 250: + click.echo("Collector name must not exceed 250 characters") return False - if port > 99999: - click.echo("Collector port number must be 5-digits or less") - return False - if maxsize > 9999: - click.echo("Maximum datagram size must be 4-digits or less") + if port < 0 or port > 65535: + click.echo("Collector port number must be between 0 and 65535") return False import socket - valid_input = True try: socket.inet_pton(socket.AF_INET, ip) except: try: socket.inet_pton(socket.AF_INET6, ip) except: - valid_input = False click.echo("Invalid collector IPv4 or IPv6 address") + return False - if agentaddr: - try: - socket.inet_pton(socket.AF_INET, agentaddr) - except: - try: - socket.inet_pton(socket.AF_INET6, agentaddr) - except: - valid_input = False - click.echo("Invalid agent IPv4 or IPv6 address") - - return valid_input + return True -def make_collector_info_dict(ip, agentaddr, port, maxsize): - return {"collector_ip" : ip, "agent_addr" : agentaddr, "collector_port": port, \ - "max_datagram_size" : maxsize} +def make_collector_info_dict(ip, port): + return {"collector_ip" : ip, "collector_port": port} # # 'sflow' command ('config sflow collector add ...') # @collector.command() -@click.option('--agentaddr', required=True, type=str, default=None, help="Agent IP address") -@click.option('--port', required=False, type=int, default=None, help="Collector port number") -@click.option('--maxsize', required=False, type=int, default=None, help="Maximum datagram size") +@click.option('--port', required=False, type=int, default=6343, help='Collector port number') @click.argument('name', required=True) @click.argument('ip', required=True) -def add(name, ip, agentaddr, port, maxsize): +def add(name, ip, port): """Add a sFlow collector""" ip = ip.lower() - if (agentaddr): - agentaddr = agentaddr.lower() - if not is_valid_collector_info(name, ip , agentaddr, port, maxsize): + if not is_valid_collector_info(name, ip, port): return config_db = ConfigDBConnector() config_db.connect() - config_db.set_entry('SFLOW_COLLECTOR', name, make_collector_info_dict(ip, agentaddr, port, maxsize)) + collector_tbl = config_db.get_table('SFLOW_COLLECTOR') + if (collector_tbl and len(collector_tbl) == 2): + click.echo("Only 2 collectors can be configured, please delete one") + return + config_db.set_entry('SFLOW_COLLECTOR', name, make_collector_info_dict(ip, port)) return +# +# 'sflow agent-id' group +# +@sflow.group('agent-id') +def agent_id(): + """Add/Delete a sFlow agent""" + pass + +# +# 'sflow' command ('config sflow agent-id add ...') +# +@agent_id.command() +@click.argument('name', required=True) +def add(name): + """Add sFlow agent information""" + if not valid_intf(name): + click.echo("Invalid interface name") + return + config_db = ConfigDBConnector() + config_db.connect() + sflow_tbl = config_db.get_table('SFLOW') + if not sflow_tbl: + click.echo("sFlow not configured.") + return + sflow_tbl['global']['agent_id'] = name + config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) + + +# +# 'sflow' command ('config sflow agent-id del ...') +# +@agent_id.command('del') +def delete(): + """Delete sFlow agent information""" + config_db = ConfigDBConnector() + config_db.connect() + sflow_tbl = config_db.get_table('SFLOW') + if not sflow_tbl: + click.echo("sFlow not configured.") + return + sflow_tbl['global'].pop('agent_id') + config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) + pass if __name__ == '__main__': config() diff --git a/show/main.py b/show/main.py index d217cd598c..5f784a52c1 100755 --- a/show/main.py +++ b/show/main.py @@ -1375,17 +1375,6 @@ def ntp(verbose): run_command(cmd, display_cmd=verbose) -# 'sflow' subcommand ("show runningconfiguration sflow") -@runningconfiguration.command() -@click.option('--verbose', is_flag=True, help="Enable verbose output") -def sflow(verbose): - """Show sFlow running configuration""" - cmd = "cat /etc/sflow.conf" - run_command(cmd, display_cmd=verbose) - cmd = "cat /etc/hsflow.conf" - run_command(cmd, display_cmd=verbose) - - # # 'startupconfiguration' group ("show startupconfiguration ...") # @@ -1686,27 +1675,90 @@ def policer(policer_name, verbose): run_command(cmd, display_cmd=verbose) +@cli.group() +def sflow(): + """Show sFlow related information""" + pass + +def show_sflow_interface(config_db): + sflow_global = config_db.get_table('SFLOW') + if not sflow_global: + click.echo("sFlow not configured") + return + port_tbl = config_db.get_table('PORT') + idx_to_port_map = {int(port_tbl[name]['index']) : name for name in port_tbl.keys()} + sflow_session_tbl = config_db.get_table('SFLOW_SESSION') + if not port_tbl: + click.echo("No ports configured") + return + click.echo("\n\tsFlow interface configurations") + click.echo("\t\tInterface\t\tAdmin State\t\tSampling Rate") + click.echo("\t\t==============================================================") + for idx in sorted(idx_to_port_map.keys()): + pname = idx_to_port_map[idx] + click.echo("\t\t{}".format(pname), nl=False) + if sflow_session_tbl and pname in sflow_session_tbl.keys(): + if 'admin_state' in sflow_session_tbl[pname].keys(): + click.echo("\t\t{}".format(sflow_session_tbl[pname]['admin_state']), nl=False) + else: + click.echo("\t\t{}".format(sflow_global['global']['admin_state']), nl=False) + else: + click.echo("\t\t{}".format(sflow_global['global']['admin_state']), nl=False) + #TODO get sample-rate from appsDB + click.echo("\t\t\t{}".format(port_tbl[pname]['speed'])) + +def show_sflow_global(config_db): + sflow_info = config_db.get_table('SFLOW') + if not sflow_info: + click.echo("sFlow not configured") + return + default_polling = 20 + default_agent = 'default' + click.echo("\n\tSFlow Global Information:") + click.echo("\t\tSFlow Admin State: \t\t\t: {}".format(sflow_info['global']['admin_state'])) + + click.echo("\t\tSFlow Polling Interval: ", nl=False) + if ('polling_interval' in sflow_info['global'].keys()): + click.echo("\t\t: {}".format(sflow_info['global']['polling_interval'])) + else: + click.echo("\t\t: {}".format(default_polling)) + + click.echo("\t\tSFlow AgentID: ", nl=False) + if ('agent_id' in sflow_info['global'].keys()): + click.echo("\t\t\t\t: {}".format(sflow_info['global']['agent_id'])) + else: + click.echo("\t\t\t\t: {}".format(default_agent)) + + sflow_info = config_db.get_table('SFLOW_COLLECTOR') + click.echo("\n\t\t{} Collectors configured:".format(len(sflow_info))) + for collector_name in sflow_info.keys(): + click.echo("\t\t\tCollector IP addr: {}\t UDP port:{}".format(sflow_info[collector_name]['collector_ip'], sflow_info[collector_name]['collector_port'])) + # -# 'sFlow' command ("show sflow") +# 'sflow command ("show sflow ...") # -@cli.command() -def sFlow(): - """Show sFlow related information""" +@sflow.command('interface') +def sflow_interface(): + """Show sFlow interface information""" config_db = ConfigDBConnector() config_db.connect() - sflow_info = config_db.get_table('SFLOW_CONFIG') - if sflow_info: - click.echo("\n\tSample-rate(s):") - for key in sflow_info: - click.echo("\t\tName: {}\t\t\tSample-rate: {}".format(key, sflow_info[key]['sample_rate'])) - sflow_info = config_db.get_table('SFLOW_COLLECTOR') - if sflow_info: - click.echo("\n\tsFlow Collector(s) information:") - for key in sorted(sflow_info): - click.echo("\t\tName: {}".format(key)) - for info_key in sflow_info[key]: - click.echo("\t\t\t{}: \t{}".format(info_key, sflow_info[key][info_key])) + show_sflow_interface(config_db) + +@sflow.command('global') +def sflow_global(): + """Show global sFlow information""" + config_db = ConfigDBConnector() + config_db.connect() + show_sflow_global(config_db) + +@sflow.command('all') +def sflow_all(): + """Show all sFlow related information""" + config_db = ConfigDBConnector() + config_db.connect() + show_sflow_global(config_db) + show_sflow_interface(config_db) # # 'acl' group ### From d00dddd2023450fa7f0c133920164bbd6c5862a4 Mon Sep 17 00:00:00 2001 From: Garrick He Date: Wed, 17 Jul 2019 16:26:29 -0700 Subject: [PATCH 03/15] Query AppsDB for sample-rate * Added logic to query AppsDB for sample-rate * Updated CLI to remove 'global' and 'all' option * Added logic to handle 'all' as interface name Signed-off-by: Garrick He --- config/main.py | 6 +++--- show/main.py | 47 ++++++++++++++++++++++++++--------------------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/config/main.py b/config/main.py index aca6b52195..3b05c13634 100755 --- a/config/main.py +++ b/config/main.py @@ -1306,7 +1306,7 @@ def interface(): @interface.command() @click.argument('name', required=True, type=str) def enable(name): - if not valid_intf(name): + if not valid_intf(name) and name != 'all': click.echo("Invalid interface name") return config_db = ConfigDBConnector() @@ -1326,7 +1326,7 @@ def enable(name): @interface.command() @click.argument('name', required=True, type=str) def disable(name): - if not valid_intf(name): + if not valid_intf(name) and name != 'all': click.echo("Invalid interface name") return config_db = ConfigDBConnector() @@ -1360,7 +1360,7 @@ def sample_rate(name, rate): if sess_dict and name in sess_dict.keys(): sess_dict[name]['sample_rate'] = rate config_db.set_entry('SFLOW_SESSION', name, sess_dict[name]) - elif sess_dict: + else: config_db.set_entry('SFLOW_SESSION', name, {'sample_rate' : rate}) # diff --git a/show/main.py b/show/main.py index 5f784a52c1..b83aa7dd42 100755 --- a/show/main.py +++ b/show/main.py @@ -1675,13 +1675,30 @@ def policer(policer_name, verbose): run_command(cmd, display_cmd=verbose) -@cli.group() + +@cli.group(invoke_without_command=True) def sflow(): """Show sFlow related information""" - pass + config_db = ConfigDBConnector() + config_db.connect() + show_sflow_global(config_db) + +def sflow_appDB_connect(): + db = SonicV2Connector(host='127.0.0.1') + db.connect(db.APPL_DB, False) + keys = db.keys(db.APPL_DB, 'SFLOW_SAMPLE_RATE_TABLE:*') + if not keys: + click.echo('No sampling rate information found in apps_db') + return None + return db.get_all(db.APPL_DB, keys[0]) + def show_sflow_interface(config_db): sflow_global = config_db.get_table('SFLOW') + sflow_sampling_tbl = sflow_appDB_connect() + if not sflow_sampling_tbl: + click.echo("sflow AppDB error") + return if not sflow_global: click.echo("sFlow not configured") return @@ -1702,10 +1719,13 @@ def show_sflow_interface(config_db): click.echo("\t\t{}".format(sflow_session_tbl[pname]['admin_state']), nl=False) else: click.echo("\t\t{}".format(sflow_global['global']['admin_state']), nl=False) + if 'sample_rate' in sflow_session_tbl[pname].keys(): + click.echo("\t\t\t{}".format(sflow_session_tbl[pname]['sample_rate'])) + else: + click.echo("\t\t\t{}".format(sflow_sampling_tbl[port_tbl[pname]['speed']])) else: - click.echo("\t\t{}".format(sflow_global['global']['admin_state']), nl=False) - #TODO get sample-rate from appsDB - click.echo("\t\t\t{}".format(port_tbl[pname]['speed'])) + click.echo("\t\t{}".format(sflow_global['global']['admin_state']), nl=False) + click.echo("\t\t\t{}".format(sflow_sampling_tbl[port_tbl[pname]['speed']])) def show_sflow_global(config_db): sflow_info = config_db.get_table('SFLOW') @@ -1735,7 +1755,7 @@ def show_sflow_global(config_db): click.echo("\t\t\tCollector IP addr: {}\t UDP port:{}".format(sflow_info[collector_name]['collector_ip'], sflow_info[collector_name]['collector_port'])) # -# 'sflow command ("show sflow ...") +# 'sflow command ("show sflow interface ...") # @sflow.command('interface') def sflow_interface(): @@ -1745,21 +1765,6 @@ def sflow_interface(): show_sflow_interface(config_db) -@sflow.command('global') -def sflow_global(): - """Show global sFlow information""" - config_db = ConfigDBConnector() - config_db.connect() - show_sflow_global(config_db) - -@sflow.command('all') -def sflow_all(): - """Show all sFlow related information""" - config_db = ConfigDBConnector() - config_db.connect() - show_sflow_global(config_db) - show_sflow_interface(config_db) - # # 'acl' group ### # From 0a268aff6e87e666d106aef57f0b453c7945364b Mon Sep 17 00:00:00 2001 From: Garrick He Date: Mon, 22 Jul 2019 11:30:18 -0700 Subject: [PATCH 04/15] Fix minor display bug * Fixed a minor display bug in the 'show sflow interface' command where the global information will also be displayed. Signed-off-by: Garrick He --- show/main.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/show/main.py b/show/main.py index b83aa7dd42..c80d50c641 100755 --- a/show/main.py +++ b/show/main.py @@ -1677,11 +1677,13 @@ def policer(policer_name, verbose): @cli.group(invoke_without_command=True) -def sflow(): +@click.pass_context +def sflow(ctx): """Show sFlow related information""" config_db = ConfigDBConnector() config_db.connect() - show_sflow_global(config_db) + if ctx.invoked_subcommand is None: + show_sflow_global(config_db) def sflow_appDB_connect(): db = SonicV2Connector(host='127.0.0.1') From bb54a87bbe6898cf24c944807d3d8a031471e704 Mon Sep 17 00:00:00 2001 From: Garrick He Date: Wed, 24 Jul 2019 09:49:42 -0700 Subject: [PATCH 05/15] Add sflow container to restart list * Add sflow container to the restart service list Signed-off-by: Garrick He --- config/main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/config/main.py b/config/main.py index 3b05c13634..fcfce195da 100755 --- a/config/main.py +++ b/config/main.py @@ -319,6 +319,7 @@ def _restart_services(): 'snmp', 'dhcp_relay', 'hostcfgd', + 'sflow', ] for service in services: try: From ad4a0afed5782c350a0fd7cac8f7790d6d129fd2 Mon Sep 17 00:00:00 2001 From: Garrick He Date: Thu, 25 Jul 2019 12:57:38 -0700 Subject: [PATCH 06/15] Code clean-up * Add comments and some clean-ups Signed-off-by: Garrick He --- config/main.py | 10 +++------- show/main.py | 4 +++- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/config/main.py b/config/main.py index fcfce195da..ebe00cc0bd 100755 --- a/config/main.py +++ b/config/main.py @@ -1238,6 +1238,7 @@ def naming_mode_alias(): def sflow(): """sFlow-related configuration tasks""" pass + # # 'sflow' command ('config sflow enable ...') # @@ -1269,7 +1270,7 @@ def disable(): config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) # -# 'sflow' command ('config sflow disable ...') +# 'sflow' command ('config sflow polling-interval ...') # @sflow.command('polling-interval') @click.argument('intv', required=True, type=int) @@ -1320,7 +1321,6 @@ def enable(name): else: config_db.set_entry('SFLOW_SESSION', name, {'admin_state' : 'enable'}) - # # 'sflow' command ('config sflow interface disable ...') # @@ -1340,7 +1340,6 @@ def disable(name): else: config_db.set_entry('SFLOW_SESSION', name, {'admin_state' : 'disable'}) - # # 'sflow' command ('config sflow interface sample-rate ...') # @@ -1372,7 +1371,6 @@ def collector(): """Add/Delete a sFlow collector""" pass - # # 'sflow' command ('config sflow collector del ...') # @@ -1384,7 +1382,6 @@ def del_collector(name): config_db.connect() config_db.set_entry('SFLOW_COLLECTOR', name, None) - def is_valid_collector_info(name, ip, port): if len(name) > 250: click.echo("Collector name must not exceed 250 characters") @@ -1408,7 +1405,6 @@ def is_valid_collector_info(name, ip, port): def make_collector_info_dict(ip, port): return {"collector_ip" : ip, "collector_port": port} - # # 'sflow' command ('config sflow collector add ...') # @@ -1459,7 +1455,6 @@ def add(name): sflow_tbl['global']['agent_id'] = name config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) - # # 'sflow' command ('config sflow agent-id del ...') # @@ -1476,5 +1471,6 @@ def delete(): config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) pass + if __name__ == '__main__': config() diff --git a/show/main.py b/show/main.py index c80d50c641..6672d2127a 100755 --- a/show/main.py +++ b/show/main.py @@ -1676,6 +1676,9 @@ def policer(policer_name, verbose): run_command(cmd, display_cmd=verbose) +# +# 'sflow command ("show sflow ...") +# @cli.group(invoke_without_command=True) @click.pass_context def sflow(ctx): @@ -1694,7 +1697,6 @@ def sflow_appDB_connect(): return None return db.get_all(db.APPL_DB, keys[0]) - def show_sflow_interface(config_db): sflow_global = config_db.get_table('SFLOW') sflow_sampling_tbl = sflow_appDB_connect() From 54a1f80df194c0b8781ac7d9b382c34ff2e02d55 Mon Sep 17 00:00:00 2001 From: Garrick He Date: Thu, 25 Jul 2019 15:45:52 -0700 Subject: [PATCH 07/15] PEP8 Compliance * Fix the sFlow related code to ensure PEP8 compliance. Signed-off-by: Garrick He --- config/main.py | 36 +++++++++++++++++++++++++++--------- show/main.py | 39 ++++++++++++++++++++++++++++----------- 2 files changed, 55 insertions(+), 20 deletions(-) diff --git a/config/main.py b/config/main.py index ebe00cc0bd..b05fc1224d 100755 --- a/config/main.py +++ b/config/main.py @@ -1239,6 +1239,7 @@ def sflow(): """sFlow-related configuration tasks""" pass + # # 'sflow' command ('config sflow enable ...') # @@ -1249,11 +1250,12 @@ def enable(): config_db.connect() sflow_tbl = config_db.get_table('SFLOW') if not sflow_tbl: - sflow_tbl = {'global' : {'admin_state' : 'enable'}} + sflow_tbl = {'global': {'admin_state': 'enable'}} else: sflow_tbl['global']['admin_state'] = 'enable' config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) + # # 'sflow' command ('config sflow disable ...') # @@ -1264,11 +1266,12 @@ def disable(): config_db.connect() sflow_tbl = config_db.get_table('SFLOW') if not sflow_tbl: - sflow_tbl = {'global' : {'admin_state' : 'disable'}} + sflow_tbl = {'global': {'admin_state': 'disable'}} else: sflow_tbl['global']['admin_state'] = 'disable' config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) + # # 'sflow' command ('config sflow polling-interval ...') # @@ -1287,13 +1290,16 @@ def polling_int(intv): sflow_tbl['global']['polling_interval'] = intv config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) + def is_valid_sample_rate(rate): return rate >= 256 and rate <= 8388608 + def valid_intf(intf): interfaces = netifaces.interfaces() return intf in interfaces + # # 'sflow interface' group # @@ -1302,6 +1308,7 @@ def interface(): """Configure sFlow on interface""" pass + # # 'sflow' command ('config sflow interface enable ...') # @@ -1319,7 +1326,8 @@ def enable(name): intf_dict[name]['admin_state'] = 'enable' config_db.set_entry('SFLOW_SESSION', name, intf_dict[name]) else: - config_db.set_entry('SFLOW_SESSION', name, {'admin_state' : 'enable'}) + config_db.set_entry('SFLOW_SESSION', name, {'admin_state': 'enable'}) + # # 'sflow' command ('config sflow interface disable ...') @@ -1338,7 +1346,8 @@ def disable(name): intf_dict[name]['admin_state'] = 'disable' config_db.set_entry('SFLOW_SESSION', name, intf_dict[name]) else: - config_db.set_entry('SFLOW_SESSION', name, {'admin_state' : 'disable'}) + config_db.set_entry('SFLOW_SESSION', name, {'admin_state': 'disable'}) + # # 'sflow' command ('config sflow interface sample-rate ...') @@ -1361,7 +1370,8 @@ def sample_rate(name, rate): sess_dict[name]['sample_rate'] = rate config_db.set_entry('SFLOW_SESSION', name, sess_dict[name]) else: - config_db.set_entry('SFLOW_SESSION', name, {'sample_rate' : rate}) + config_db.set_entry('SFLOW_SESSION', name, {'sample_rate': rate}) + # # 'sflow collector' group @@ -1371,6 +1381,7 @@ def collector(): """Add/Delete a sFlow collector""" pass + # # 'sflow' command ('config sflow collector del ...') # @@ -1382,6 +1393,7 @@ def del_collector(name): config_db.connect() config_db.set_entry('SFLOW_COLLECTOR', name, None) + def is_valid_collector_info(name, ip, port): if len(name) > 250: click.echo("Collector name must not exceed 250 characters") @@ -1402,17 +1414,19 @@ def is_valid_collector_info(name, ip, port): return True + def make_collector_info_dict(ip, port): - return {"collector_ip" : ip, "collector_port": port} + return {"collector_ip": ip, "collector_port": port} + # # 'sflow' command ('config sflow collector add ...') # @collector.command() -@click.option('--port', required=False, type=int, default=6343, help='Collector port number') +@click.option('--port', required=False, type=int, default=6343, + help='Collector port number') @click.argument('name', required=True) @click.argument('ip', required=True) - def add(name, ip, port): """Add a sFlow collector""" ip = ip.lower() @@ -1425,9 +1439,11 @@ def add(name, ip, port): if (collector_tbl and len(collector_tbl) == 2): click.echo("Only 2 collectors can be configured, please delete one") return - config_db.set_entry('SFLOW_COLLECTOR', name, make_collector_info_dict(ip, port)) + config_db.set_entry('SFLOW_COLLECTOR', name, + make_collector_info_dict(ip, port)) return + # # 'sflow agent-id' group # @@ -1436,6 +1452,7 @@ def agent_id(): """Add/Delete a sFlow agent""" pass + # # 'sflow' command ('config sflow agent-id add ...') # @@ -1455,6 +1472,7 @@ def add(name): sflow_tbl['global']['agent_id'] = name config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) + # # 'sflow' command ('config sflow agent-id del ...') # diff --git a/show/main.py b/show/main.py index 6672d2127a..ea7a061591 100755 --- a/show/main.py +++ b/show/main.py @@ -1688,6 +1688,7 @@ def sflow(ctx): if ctx.invoked_subcommand is None: show_sflow_global(config_db) + def sflow_appDB_connect(): db = SonicV2Connector(host='127.0.0.1') db.connect(db.APPL_DB, False) @@ -1697,6 +1698,7 @@ def sflow_appDB_connect(): return None return db.get_all(db.APPL_DB, keys[0]) + def show_sflow_interface(config_db): sflow_global = config_db.get_table('SFLOW') sflow_sampling_tbl = sflow_appDB_connect() @@ -1707,29 +1709,38 @@ def show_sflow_interface(config_db): click.echo("sFlow not configured") return port_tbl = config_db.get_table('PORT') - idx_to_port_map = {int(port_tbl[name]['index']) : name for name in port_tbl.keys()} + idx_to_port_map = {int(port_tbl[name]['index']): name for name in + port_tbl.keys()} sflow_session_tbl = config_db.get_table('SFLOW_SESSION') if not port_tbl: click.echo("No ports configured") return click.echo("\n\tsFlow interface configurations") click.echo("\t\tInterface\t\tAdmin State\t\tSampling Rate") - click.echo("\t\t==============================================================") + click.echo("\t\t=====================================================" + + "=========") for idx in sorted(idx_to_port_map.keys()): pname = idx_to_port_map[idx] click.echo("\t\t{}".format(pname), nl=False) if sflow_session_tbl and pname in sflow_session_tbl.keys(): if 'admin_state' in sflow_session_tbl[pname].keys(): - click.echo("\t\t{}".format(sflow_session_tbl[pname]['admin_state']), nl=False) + click.echo("\t\t{}".format(sflow_session_tbl[pname] + ['admin_state']), nl=False) else: - click.echo("\t\t{}".format(sflow_global['global']['admin_state']), nl=False) + click.echo("\t\t{}".format(sflow_global['global'] + ['admin_state']), nl=False) if 'sample_rate' in sflow_session_tbl[pname].keys(): - click.echo("\t\t\t{}".format(sflow_session_tbl[pname]['sample_rate'])) + click.echo("\t\t\t{}".format(sflow_session_tbl[pname] + ['sample_rate'])) else: - click.echo("\t\t\t{}".format(sflow_sampling_tbl[port_tbl[pname]['speed']])) + click.echo("\t\t\t{}".format(sflow_sampling_tbl[port_tbl + [pname]['speed']])) else: - click.echo("\t\t{}".format(sflow_global['global']['admin_state']), nl=False) - click.echo("\t\t\t{}".format(sflow_sampling_tbl[port_tbl[pname]['speed']])) + click.echo("\t\t{}".format(sflow_global['global'] + ['admin_state']), nl=False) + click.echo("\t\t\t{}".format(sflow_sampling_tbl[port_tbl + [pname]['speed']])) + def show_sflow_global(config_db): sflow_info = config_db.get_table('SFLOW') @@ -1739,11 +1750,14 @@ def show_sflow_global(config_db): default_polling = 20 default_agent = 'default' click.echo("\n\tSFlow Global Information:") - click.echo("\t\tSFlow Admin State: \t\t\t: {}".format(sflow_info['global']['admin_state'])) + click.echo("\t\tSFlow Admin State: \t\t\t: {}".format(sflow_info + ['global'] + ['admin_state'])) click.echo("\t\tSFlow Polling Interval: ", nl=False) if ('polling_interval' in sflow_info['global'].keys()): - click.echo("\t\t: {}".format(sflow_info['global']['polling_interval'])) + click.echo("\t\t: {}".format(sflow_info['global'] + ['polling_interval'])) else: click.echo("\t\t: {}".format(default_polling)) @@ -1756,7 +1770,10 @@ def show_sflow_global(config_db): sflow_info = config_db.get_table('SFLOW_COLLECTOR') click.echo("\n\t\t{} Collectors configured:".format(len(sflow_info))) for collector_name in sflow_info.keys(): - click.echo("\t\t\tCollector IP addr: {}\t UDP port:{}".format(sflow_info[collector_name]['collector_ip'], sflow_info[collector_name]['collector_port'])) + click.echo("\t\t\tCollector IP addr: {}\t UDP port:{}".format( + sflow_info[collector_name]['collector_ip'], + sflow_info[collector_name]['collector_port'])) + # # 'sflow command ("show sflow interface ...") From b5fcb0732e4f2f3d6aedc72ad650fb26d6c5a02f Mon Sep 17 00:00:00 2001 From: Garrick He Date: Tue, 30 Jul 2019 16:02:03 -0700 Subject: [PATCH 08/15] Address code-review comments * Address code review comments * Re-run PEP8 compliance check Signed-off-by: Garrick He --- config/main.py | 244 +++++++++++++++++++++++++++---------------------- show/main.py | 82 ++++++++--------- 2 files changed, 173 insertions(+), 153 deletions(-) diff --git a/config/main.py b/config/main.py index b05fc1224d..42a92cbebd 100755 --- a/config/main.py +++ b/config/main.py @@ -1235,170 +1235,212 @@ def naming_mode_alias(): # 'sflow' group ('config sflow ...') # @config.group() -def sflow(): +@click.pass_context +def sflow(ctx): """sFlow-related configuration tasks""" + config_db = ConfigDBConnector() + config_db.connect() + ctx.obj = {'db': config_db} pass - # -# 'sflow' command ('config sflow enable ...') +# 'sflow' command ('config sflow enable') # @sflow.command() -def enable(): +@click.pass_context +def enable(ctx): """Enable sFlow""" - config_db = ConfigDBConnector() - config_db.connect() + config_db = ctx.obj['db'] sflow_tbl = config_db.get_table('SFLOW') + if not sflow_tbl: sflow_tbl = {'global': {'admin_state': 'enable'}} else: sflow_tbl['global']['admin_state'] = 'enable' - config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) + config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) # -# 'sflow' command ('config sflow disable ...') +# 'sflow' command ('config sflow disable') # @sflow.command() -def disable(): +@click.pass_context +def disable(ctx): """Disable sFlow""" - config_db = ConfigDBConnector() - config_db.connect() + config_db = ctx.obj['db'] sflow_tbl = config_db.get_table('SFLOW') + if not sflow_tbl: sflow_tbl = {'global': {'admin_state': 'disable'}} else: sflow_tbl['global']['admin_state'] = 'disable' - config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) + config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) # # 'sflow' command ('config sflow polling-interval ...') # @sflow.command('polling-interval') -@click.argument('intv', required=True, type=int) -def polling_int(intv): +@click.argument('interval', metavar='', required=True, + type=int) +@click.pass_context +def polling_int(ctx, interval): """Set polling-interval for counter-sampling (0 to disable)""" - if (0 > intv or intv > 300): + if interval not in range(0, 300 + 1): click.echo("Polling interval must be between 0-300") - config_db = ConfigDBConnector() - config_db.connect() + + config_db = ctx.obj['db'] sflow_tbl = config_db.get_table('SFLOW') + if not sflow_tbl: click.echo("sFlow not configured") return - sflow_tbl['global']['polling_interval'] = intv - config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) + sflow_tbl['global']['polling_interval'] = interval + config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) def is_valid_sample_rate(rate): - return rate >= 256 and rate <= 8388608 - - -def valid_intf(intf): - interfaces = netifaces.interfaces() - return intf in interfaces + return rate in range(256, 8388608 + 1) # # 'sflow interface' group # @sflow.group() -def interface(): - """Configure sFlow on interface""" +@click.pass_context +def interface(ctx): + """Configure sFlow settings for an interface""" pass - # # 'sflow' command ('config sflow interface enable ...') # @interface.command() -@click.argument('name', required=True, type=str) -def enable(name): - if not valid_intf(name) and name != 'all': +@click.argument('ifname', metavar='', required=True, type=str) +@click.pass_context +def enable(ctx, ifname): + if not interface_name_is_valid(ifname) and ifname != 'all': click.echo("Invalid interface name") return - config_db = ConfigDBConnector() - config_db.connect() + config_db = ctx.obj['db'] intf_dict = config_db.get_table('SFLOW_SESSION') - if intf_dict and name in intf_dict.keys(): - intf_dict[name]['admin_state'] = 'enable' - config_db.set_entry('SFLOW_SESSION', name, intf_dict[name]) - else: - config_db.set_entry('SFLOW_SESSION', name, {'admin_state': 'enable'}) + if intf_dict and ifname in intf_dict.keys(): + intf_dict[ifname]['admin_state'] = 'enable' + config_db.set_entry('SFLOW_SESSION', ifname, intf_dict[ifname]) + else: + config_db.set_entry('SFLOW_SESSION', ifname, {'admin_state': 'enable'}) # # 'sflow' command ('config sflow interface disable ...') # @interface.command() -@click.argument('name', required=True, type=str) -def disable(name): - if not valid_intf(name) and name != 'all': +@click.argument('ifname', metavar='', required=True, type=str) +@click.pass_context +def disable(ctx, ifname): + if not interface_name_is_valid(ifname) and ifname != 'all': click.echo("Invalid interface name") return - config_db = ConfigDBConnector() - config_db.connect() + + config_db = ctx.obj['db'] intf_dict = config_db.get_table('SFLOW_SESSION') - if intf_dict and name in intf_dict.keys(): - intf_dict[name]['admin_state'] = 'disable' - config_db.set_entry('SFLOW_SESSION', name, intf_dict[name]) + if intf_dict and ifname in intf_dict.keys(): + intf_dict[ifname]['admin_state'] = 'disable' + config_db.set_entry('SFLOW_SESSION', ifname, intf_dict[ifname]) else: - config_db.set_entry('SFLOW_SESSION', name, {'admin_state': 'disable'}) - + config_db.set_entry('SFLOW_SESSION', ifname, + {'admin_state': 'disable'}) # # 'sflow' command ('config sflow interface sample-rate ...') # @interface.command('sample-rate') -@click.argument('name', required=True, type=str) -@click.argument('rate', required=True, type=int) -def sample_rate(name, rate): - if not valid_intf(name): +@click.argument('ifname', metavar='', required=True, type=str) +@click.argument('rate', metavar='', required=True, type=int) +@click.pass_context +def sample_rate(ctx, ifname, rate): + if not interface_name_is_valid(ifname): click.echo('Invalid interface name') return if not is_valid_sample_rate(rate): click.echo('Error: Sample rate must be between 256 and 8388608') return - config_db = ConfigDBConnector() - config_db.connect() + config_db = ctx.obj['db'] sess_dict = config_db.get_table('SFLOW_SESSION') - if sess_dict and name in sess_dict.keys(): - sess_dict[name]['sample_rate'] = rate - config_db.set_entry('SFLOW_SESSION', name, sess_dict[name]) + + if sess_dict and ifname in sess_dict.keys(): + sess_dict[ifname]['sample_rate'] = rate + config_db.set_entry('SFLOW_SESSION', ifname, sess_dict[ifname]) else: - config_db.set_entry('SFLOW_SESSION', name, {'sample_rate': rate}) + config_db.set_entry('SFLOW_SESSION', ifname, {'sample_rate': rate}) # # 'sflow collector' group # @sflow.group() -def collector(): +@click.pass_context +def collector(ctx): """Add/Delete a sFlow collector""" pass +# +# 'sflow' command ('config sflow collector add ...') +# +@collector.command() +@click.option('--port', required=False, type=int, default=6343, + help='Collector port number') +@click.argument('name', metavar='', required=True) +@click.argument('ipaddr', metavar='', required=True) +@click.pass_context +def add(ctx, name, ipaddr, port): + """Add a sFlow collector""" + ipaddr = ipaddr.lower() + + if not is_valid_collector_info(name, ipaddr, port): + return + + config_db = ctx.obj['db'] + collector_tbl = config_db.get_table('SFLOW_COLLECTOR') + + if (collector_tbl and len(collector_tbl) == 2): + click.echo("Only 2 collectors can be configured, please delete one") + return + if name in collector_tbl.keys(): + click.echo("Collector {} already configured. Please delete it first". + format(name)) + return + + config_db.set_entry('SFLOW_COLLECTOR', name, + {"collector_ip": ipaddr, "collector_port": port}) + return # # 'sflow' command ('config sflow collector del ...') # @collector.command('del') -@click.argument('name', required=True) -def del_collector(name): +@click.argument('name', metavar='', required=True) +@click.pass_context +def del_collector(ctx, name): """Delete a sFlow collector""" - config_db = ConfigDBConnector() - config_db.connect() - config_db.set_entry('SFLOW_COLLECTOR', name, None) + config_db = ctx.obj['db'] + collector_tbl = config_db.get_table('SFLOW_COLLECTOR') + + if name not in collector_tbl.keys(): + click.echo("Collector: {} not configured".format(name)) + return + config_db.set_entry('SFLOW_COLLECTOR', name, None) def is_valid_collector_info(name, ip, port): - if len(name) > 250: - click.echo("Collector name must not exceed 250 characters") + if len(name) > 16: + click.echo("Collector name must not exceed 16 characters") return False - if port < 0 or port > 65535: + + if port not in range(0, 65535 + 1): click.echo("Collector port number must be between 0 and 65535") return False @@ -1415,79 +1457,61 @@ def is_valid_collector_info(name, ip, port): return True -def make_collector_info_dict(ip, port): - return {"collector_ip": ip, "collector_port": port} - - -# -# 'sflow' command ('config sflow collector add ...') -# -@collector.command() -@click.option('--port', required=False, type=int, default=6343, - help='Collector port number') -@click.argument('name', required=True) -@click.argument('ip', required=True) -def add(name, ip, port): - """Add a sFlow collector""" - ip = ip.lower() - if not is_valid_collector_info(name, ip, port): - return - - config_db = ConfigDBConnector() - config_db.connect() - collector_tbl = config_db.get_table('SFLOW_COLLECTOR') - if (collector_tbl and len(collector_tbl) == 2): - click.echo("Only 2 collectors can be configured, please delete one") - return - config_db.set_entry('SFLOW_COLLECTOR', name, - make_collector_info_dict(ip, port)) - return - - # # 'sflow agent-id' group # @sflow.group('agent-id') -def agent_id(): +@click.pass_context +def agent_id(ctx): """Add/Delete a sFlow agent""" pass - # # 'sflow' command ('config sflow agent-id add ...') # @agent_id.command() -@click.argument('name', required=True) -def add(name): +@click.argument('ifname', metavar='', required=True) +@click.pass_context +def add(ctx, ifname): """Add sFlow agent information""" - if not valid_intf(name): + if not interface_name_is_valid(ifname): click.echo("Invalid interface name") return - config_db = ConfigDBConnector() - config_db.connect() + + config_db = ctx.obj['db'] sflow_tbl = config_db.get_table('SFLOW') + if not sflow_tbl: click.echo("sFlow not configured.") return - sflow_tbl['global']['agent_id'] = name - config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) + if 'agent_id' in sflow_tbl['global'].keys(): + click.echo("Agent already configured. Please delete it first.") + return + + sflow_tbl['global']['agent_id'] = ifname + config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) # -# 'sflow' command ('config sflow agent-id del ...') +# 'sflow' command ('config sflow agent-id del') # @agent_id.command('del') -def delete(): +@click.pass_context +def delete(ctx): """Delete sFlow agent information""" - config_db = ConfigDBConnector() - config_db.connect() + config_db = ctx.obj['db'] sflow_tbl = config_db.get_table('SFLOW') + if not sflow_tbl: click.echo("sFlow not configured.") return + + if 'agent_id' not in sflow_tbl['global'].keys(): + click.echo("sFlow agent not configured.") + return + sflow_tbl['global'].pop('agent_id') config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) - pass if __name__ == '__main__': diff --git a/show/main.py b/show/main.py index ea7a061591..0657bde9bd 100755 --- a/show/main.py +++ b/show/main.py @@ -1685,9 +1685,18 @@ def sflow(ctx): """Show sFlow related information""" config_db = ConfigDBConnector() config_db.connect() + ctx.obj = {'db': config_db} if ctx.invoked_subcommand is None: show_sflow_global(config_db) +# +# 'sflow command ("show sflow interface ...") +# +@sflow.command('interface') +@click.pass_context +def sflow_interface(ctx): + """Show sFlow interface information""" + show_sflow_interface(ctx.obj['db']) def sflow_appDB_connect(): db = SonicV2Connector(host='127.0.0.1') @@ -1698,49 +1707,49 @@ def sflow_appDB_connect(): return None return db.get_all(db.APPL_DB, keys[0]) - def show_sflow_interface(config_db): sflow_global = config_db.get_table('SFLOW') sflow_sampling_tbl = sflow_appDB_connect() + if not sflow_sampling_tbl: click.echo("sflow AppDB error") return + if not sflow_global: click.echo("sFlow not configured") return + port_tbl = config_db.get_table('PORT') idx_to_port_map = {int(port_tbl[name]['index']): name for name in port_tbl.keys()} sflow_session_tbl = config_db.get_table('SFLOW_SESSION') + if not port_tbl: click.echo("No ports configured") return - click.echo("\n\tsFlow interface configurations") - click.echo("\t\tInterface\t\tAdmin State\t\tSampling Rate") - click.echo("\t\t=====================================================" + - "=========") + + click.echo("\nsFlow interface configurations") + header = ['Interface', 'Admin State', 'Sampling Rate'] + body = [] for idx in sorted(idx_to_port_map.keys()): pname = idx_to_port_map[idx] - click.echo("\t\t{}".format(pname), nl=False) + body_info = [pname] + if sflow_session_tbl and pname in sflow_session_tbl.keys(): if 'admin_state' in sflow_session_tbl[pname].keys(): - click.echo("\t\t{}".format(sflow_session_tbl[pname] - ['admin_state']), nl=False) + body_info.append(sflow_session_tbl[pname]['admin_state']) else: - click.echo("\t\t{}".format(sflow_global['global'] - ['admin_state']), nl=False) + body_info.append(sflow_global['global']['admin_state']) + if 'sample_rate' in sflow_session_tbl[pname].keys(): - click.echo("\t\t\t{}".format(sflow_session_tbl[pname] - ['sample_rate'])) + body_info.append(sflow_session_tbl[pname]['sample_rate']) else: - click.echo("\t\t\t{}".format(sflow_sampling_tbl[port_tbl - [pname]['speed']])) + body_info.append(sflow_sampling_tbl[port_tbl[pname]['speed']]) else: - click.echo("\t\t{}".format(sflow_global['global'] - ['admin_state']), nl=False) - click.echo("\t\t\t{}".format(sflow_sampling_tbl[port_tbl - [pname]['speed']])) - + body_info.append(sflow_global['global']['admin_state']) + body_info.append(sflow_sampling_tbl[port_tbl[pname]['speed']]) + body.append(body_info) + click.echo(tabulate(body, header, tablefmt='grid')) def show_sflow_global(config_db): sflow_info = config_db.get_table('SFLOW') @@ -1749,43 +1758,30 @@ def show_sflow_global(config_db): return default_polling = 20 default_agent = 'default' - click.echo("\n\tSFlow Global Information:") - click.echo("\t\tSFlow Admin State: \t\t\t: {}".format(sflow_info - ['global'] - ['admin_state'])) + click.echo("\nSFlow Global Information:") + click.echo(" SFlow Admin State: {}".format(sflow_info['global'] + ['admin_state'])) - click.echo("\t\tSFlow Polling Interval: ", nl=False) + click.echo(" SFlow Polling Interval: ", nl=False) if ('polling_interval' in sflow_info['global'].keys()): - click.echo("\t\t: {}".format(sflow_info['global'] - ['polling_interval'])) + click.echo(" {}".format(sflow_info['global']['polling_interval'])) else: - click.echo("\t\t: {}".format(default_polling)) + click.echo(" {}".format(default_polling)) - click.echo("\t\tSFlow AgentID: ", nl=False) + click.echo(" SFlow AgentID: ", nl=False) if ('agent_id' in sflow_info['global'].keys()): - click.echo("\t\t\t\t: {}".format(sflow_info['global']['agent_id'])) + click.echo(" {}".format(sflow_info['global']['agent_id'])) else: - click.echo("\t\t\t\t: {}".format(default_agent)) + click.echo(" {}".format(default_agent)) sflow_info = config_db.get_table('SFLOW_COLLECTOR') - click.echo("\n\t\t{} Collectors configured:".format(len(sflow_info))) + click.echo("\n {} Collectors configured:".format(len(sflow_info))) for collector_name in sflow_info.keys(): - click.echo("\t\t\tCollector IP addr: {}\t UDP port:{}".format( + click.echo(" Collector IP addr: {}\t UDP port:{}".format( sflow_info[collector_name]['collector_ip'], sflow_info[collector_name]['collector_port'])) -# -# 'sflow command ("show sflow interface ...") -# -@sflow.command('interface') -def sflow_interface(): - """Show sFlow interface information""" - config_db = ConfigDBConnector() - config_db.connect() - show_sflow_interface(config_db) - - # # 'acl' group ### # From a78643e133ed26ab47336b3e6e3545a3ca26a23f Mon Sep 17 00:00:00 2001 From: Garrick He Date: Tue, 13 Aug 2019 14:52:58 -0700 Subject: [PATCH 09/15] Fix: UT issues * Fix: Can't configure sFlow when daemon is disabled. * Fix: Interface admin-state are in correct * Fix: Collectors are shown in sorted (by name) order * Fix: Can add lo interface for sFlow agent-id * Fix: Can't ovewrite existing collector with the same name Signed-off-by: Garrick He --- config/main.py | 19 ++++++------------- show/main.py | 45 ++++++++++++++++++++++++++------------------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/config/main.py b/config/main.py index 42a92cbebd..4e1481768d 100755 --- a/config/main.py +++ b/config/main.py @@ -1293,8 +1293,7 @@ def polling_int(ctx, interval): sflow_tbl = config_db.get_table('SFLOW') if not sflow_tbl: - click.echo("sFlow not configured") - return + sflow_tbl = {'global': {'admin_state': 'disable'}} sflow_tbl['global']['polling_interval'] = interval config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) @@ -1361,7 +1360,7 @@ def disable(ctx, ifname): @click.argument('rate', metavar='', required=True, type=int) @click.pass_context def sample_rate(ctx, ifname, rate): - if not interface_name_is_valid(ifname): + if not interface_name_is_valid(ifname) and ifname != 'all': click.echo('Invalid interface name') return if not is_valid_sample_rate(rate): @@ -1406,13 +1405,9 @@ def add(ctx, name, ipaddr, port): config_db = ctx.obj['db'] collector_tbl = config_db.get_table('SFLOW_COLLECTOR') - if (collector_tbl and len(collector_tbl) == 2): + if (collector_tbl and name not in collector_tbl.keys() and len(collector_tbl) == 2): click.echo("Only 2 collectors can be configured, please delete one") return - if name in collector_tbl.keys(): - click.echo("Collector {} already configured. Please delete it first". - format(name)) - return config_db.set_entry('SFLOW_COLLECTOR', name, {"collector_ip": ipaddr, "collector_port": port}) @@ -1474,7 +1469,7 @@ def agent_id(ctx): @click.pass_context def add(ctx, ifname): """Add sFlow agent information""" - if not interface_name_is_valid(ifname): + if ifname not in netifaces.interfaces(): click.echo("Invalid interface name") return @@ -1482,8 +1477,7 @@ def add(ctx, ifname): sflow_tbl = config_db.get_table('SFLOW') if not sflow_tbl: - click.echo("sFlow not configured.") - return + sflow_tbl = {'global': {'admin_state': 'disable'}} if 'agent_id' in sflow_tbl['global'].keys(): click.echo("Agent already configured. Please delete it first.") @@ -1503,8 +1497,7 @@ def delete(ctx): sflow_tbl = config_db.get_table('SFLOW') if not sflow_tbl: - click.echo("sFlow not configured.") - return + sflow_tbl = {'global': {'admin_state': 'disable'}} if 'agent_id' not in sflow_tbl['global'].keys(): click.echo("sFlow agent not configured.") diff --git a/show/main.py b/show/main.py index 0657bde9bd..ada04b6411 100755 --- a/show/main.py +++ b/show/main.py @@ -1708,21 +1708,20 @@ def sflow_appDB_connect(): return db.get_all(db.APPL_DB, keys[0]) def show_sflow_interface(config_db): - sflow_global = config_db.get_table('SFLOW') + default_admin_state = 'enabled' sflow_sampling_tbl = sflow_appDB_connect() - if not sflow_sampling_tbl: click.echo("sflow AppDB error") return - if not sflow_global: - click.echo("sFlow not configured") - return - port_tbl = config_db.get_table('PORT') idx_to_port_map = {int(port_tbl[name]['index']): name for name in port_tbl.keys()} sflow_session_tbl = config_db.get_table('SFLOW_SESSION') + all_session_admin_state = None + if sflow_session_tbl and 'all' in sflow_session_tbl.keys(): + if 'admin_state' in sflow_session_tbl['all']: + all_session_admin_state = sflow_session_tbl['all']['admin_state'] if not port_tbl: click.echo("No ports configured") @@ -1738,46 +1737,54 @@ def show_sflow_interface(config_db): if sflow_session_tbl and pname in sflow_session_tbl.keys(): if 'admin_state' in sflow_session_tbl[pname].keys(): body_info.append(sflow_session_tbl[pname]['admin_state']) + elif all_session_admin_state is not None: + body_info.append(all_session_admin_state) else: - body_info.append(sflow_global['global']['admin_state']) + body_info.append(default_admin_state) if 'sample_rate' in sflow_session_tbl[pname].keys(): body_info.append(sflow_session_tbl[pname]['sample_rate']) else: body_info.append(sflow_sampling_tbl[port_tbl[pname]['speed']]) else: - body_info.append(sflow_global['global']['admin_state']) + if all_session_admin_state is not None: + body_info.append(all_session_admin_state) + else: + body_info.append(default_admin_state) body_info.append(sflow_sampling_tbl[port_tbl[pname]['speed']]) body.append(body_info) click.echo(tabulate(body, header, tablefmt='grid')) def show_sflow_global(config_db): - sflow_info = config_db.get_table('SFLOW') - if not sflow_info: - click.echo("sFlow not configured") - return default_polling = 20 default_agent = 'default' - click.echo("\nSFlow Global Information:") - click.echo(" SFlow Admin State: {}".format(sflow_info['global'] - ['admin_state'])) + + sflow_info = config_db.get_table('SFLOW') + global_admin_state = 'disabled' + if sflow_info: + global_admin_state = sflow_info['global']['admin_state'] + + click.echo("\nsFlow Global Information:") + click.echo(" sFlow Daemon State: {}".format(global_admin_state)) + click.echo(" SFlow Polling Interval: ", nl=False) - if ('polling_interval' in sflow_info['global'].keys()): + if (sflow_info and 'polling_interval' in sflow_info['global'].keys()): click.echo(" {}".format(sflow_info['global']['polling_interval'])) else: click.echo(" {}".format(default_polling)) click.echo(" SFlow AgentID: ", nl=False) - if ('agent_id' in sflow_info['global'].keys()): + if (sflow_info and 'agent_id' in sflow_info['global'].keys()): click.echo(" {}".format(sflow_info['global']['agent_id'])) else: click.echo(" {}".format(default_agent)) sflow_info = config_db.get_table('SFLOW_COLLECTOR') click.echo("\n {} Collectors configured:".format(len(sflow_info))) - for collector_name in sflow_info.keys(): - click.echo(" Collector IP addr: {}\t UDP port:{}".format( + for collector_name in sorted(sflow_info.keys()): + click.echo(" Name: {}\t IP addr: {}\t UDP port:{}".format( + collector_name, sflow_info[collector_name]['collector_ip'], sflow_info[collector_name]['collector_port'])) From 7e04e74b5ea9047414b6b0a355c70fc8443f83c6 Mon Sep 17 00:00:00 2001 From: Garrick He Date: Wed, 14 Aug 2019 11:09:18 -0700 Subject: [PATCH 10/15] Fix: Valid polling intervals * Fix the polling intervals to be between 5-300 and 0 to disable. Signed-off-by: Garrick He --- config/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/main.py b/config/main.py index 4e1481768d..7113e64f99 100755 --- a/config/main.py +++ b/config/main.py @@ -1286,8 +1286,8 @@ def disable(ctx): @click.pass_context def polling_int(ctx, interval): """Set polling-interval for counter-sampling (0 to disable)""" - if interval not in range(0, 300 + 1): - click.echo("Polling interval must be between 0-300") + if interval not in range(5, 300 + 1) and interval != 0: + click.echo("Polling interval must be between 5-300 (0 to disable)") config_db = ctx.obj['db'] sflow_tbl = config_db.get_table('SFLOW') From b2e555d6fd3876b8194b90a9d594b4f2bab68aa8 Mon Sep 17 00:00:00 2001 From: Garrick He Date: Thu, 15 Aug 2019 13:44:05 -0700 Subject: [PATCH 11/15] Add sFlow rollback and upgrade * Add sFlow container to support rollback and upgrade. Signed-off-by: Garrick He --- sonic_installer/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sonic_installer/main.py b/sonic_installer/main.py index 77d2cac228..b47703c736 100644 --- a/sonic_installer/main.py +++ b/sonic_installer/main.py @@ -469,7 +469,7 @@ def cleanup(): @click.option('--tag', type=str, help="Tag for the new docker image") @click.option('--warm', is_flag=True, help="Perform warm upgrade") @click.argument('container_name', metavar='', required=True, - type=click.Choice(["swss", "snmp", "lldp", "bgp", "pmon", "dhcp_relay", "telemetry", "teamd", "radv", "amon"])) + type=click.Choice(["swss", "snmp", "lldp", "bgp", "pmon", "dhcp_relay", "telemetry", "teamd", "radv", "amon", "sflow"])) @click.argument('url') def upgrade_docker(container_name, url, cleanup_image, skip_check, tag, warm): """ Upgrade docker image from local binary or URL""" @@ -632,7 +632,7 @@ def upgrade_docker(container_name, url, cleanup_image, skip_check, tag, warm): @click.option('-y', '--yes', is_flag=True, callback=abort_if_false, expose_value=False, prompt='Docker image will be rolled back, continue?') @click.argument('container_name', metavar='', required=True, - type=click.Choice(["swss", "snmp", "lldp", "bgp", "pmon", "dhcp_relay", "telemetry", "teamd", "radv", "amon"])) + type=click.Choice(["swss", "snmp", "lldp", "bgp", "pmon", "dhcp_relay", "telemetry", "teamd", "radv", "amon", "sflow"])) def rollback_docker(container_name): """ Rollback docker image to previous version""" image_name = get_container_image_name(container_name) From 6c25969ff139155481c6f318289c2f4668d9e353 Mon Sep 17 00:00:00 2001 From: Garrick He Date: Thu, 10 Oct 2019 15:41:58 -0700 Subject: [PATCH 12/15] fix: Code review comments * Use mod_entry instead of set_entry for configDB changes * Only query applDB for session information * Minor change to show command output to match HDL Signed-off-by: Garrick He --- config/main.py | 65 ++++++++++++++++++++++---------------------------- show/main.py | 58 +++++++++++++------------------------------- 2 files changed, 45 insertions(+), 78 deletions(-) diff --git a/config/main.py b/config/main.py index 7181de69be..0169dfc2c9 100755 --- a/config/main.py +++ b/config/main.py @@ -1342,7 +1342,7 @@ def enable(ctx): else: sflow_tbl['global']['admin_state'] = 'enable' - config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) + config_db.mod_entry('SFLOW', 'global', sflow_tbl['global']) # # 'sflow' command ('config sflow disable') @@ -1359,7 +1359,7 @@ def disable(ctx): else: sflow_tbl['global']['admin_state'] = 'disable' - config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) + config_db.mod_entry('SFLOW', 'global', sflow_tbl['global']) # # 'sflow' command ('config sflow polling-interval ...') @@ -1370,7 +1370,7 @@ def disable(ctx): @click.pass_context def polling_int(ctx, interval): """Set polling-interval for counter-sampling (0 to disable)""" - if interval not in range(5, 300 + 1) and interval != 0: + if interval not in range(5, 301) and interval != 0: click.echo("Polling interval must be between 5-300 (0 to disable)") config_db = ctx.obj['db'] @@ -1380,7 +1380,7 @@ def polling_int(ctx, interval): sflow_tbl = {'global': {'admin_state': 'disable'}} sflow_tbl['global']['polling_interval'] = interval - config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) + config_db.mod_entry('SFLOW', 'global', sflow_tbl['global']) def is_valid_sample_rate(rate): return rate in range(256, 8388608 + 1) @@ -1411,9 +1411,9 @@ def enable(ctx, ifname): if intf_dict and ifname in intf_dict.keys(): intf_dict[ifname]['admin_state'] = 'enable' - config_db.set_entry('SFLOW_SESSION', ifname, intf_dict[ifname]) + config_db.mod_entry('SFLOW_SESSION', ifname, intf_dict[ifname]) else: - config_db.set_entry('SFLOW_SESSION', ifname, {'admin_state': 'enable'}) + config_db.mod_entry('SFLOW_SESSION', ifname, {'admin_state': 'enable'}) # # 'sflow' command ('config sflow interface disable ...') @@ -1431,9 +1431,9 @@ def disable(ctx, ifname): if intf_dict and ifname in intf_dict.keys(): intf_dict[ifname]['admin_state'] = 'disable' - config_db.set_entry('SFLOW_SESSION', ifname, intf_dict[ifname]) + config_db.mod_entry('SFLOW_SESSION', ifname, intf_dict[ifname]) else: - config_db.set_entry('SFLOW_SESSION', ifname, + config_db.mod_entry('SFLOW_SESSION', ifname, {'admin_state': 'disable'}) # @@ -1456,9 +1456,9 @@ def sample_rate(ctx, ifname, rate): if sess_dict and ifname in sess_dict.keys(): sess_dict[ifname]['sample_rate'] = rate - config_db.set_entry('SFLOW_SESSION', ifname, sess_dict[ifname]) + config_db.mod_entry('SFLOW_SESSION', ifname, sess_dict[ifname]) else: - config_db.set_entry('SFLOW_SESSION', ifname, {'sample_rate': rate}) + config_db.mod_entry('SFLOW_SESSION', ifname, {'sample_rate': rate}) # @@ -1470,6 +1470,21 @@ def collector(ctx): """Add/Delete a sFlow collector""" pass +def is_valid_collector_info(name, ip, port): + if len(name) > 16: + click.echo("Collector name must not exceed 16 characters") + return False + + if port not in range(0, 65535 + 1): + click.echo("Collector port number must be between 0 and 65535") + return False + + if not is_ipaddress(ip): + click.echo("Invalid IP address") + return False + + return True + # # 'sflow' command ('config sflow collector add ...') # @@ -1493,7 +1508,7 @@ def add(ctx, name, ipaddr, port): click.echo("Only 2 collectors can be configured, please delete one") return - config_db.set_entry('SFLOW_COLLECTOR', name, + config_db.mod_entry('SFLOW_COLLECTOR', name, {"collector_ip": ipaddr, "collector_port": port}) return @@ -1512,29 +1527,7 @@ def del_collector(ctx, name): click.echo("Collector: {} not configured".format(name)) return - config_db.set_entry('SFLOW_COLLECTOR', name, None) - -def is_valid_collector_info(name, ip, port): - if len(name) > 16: - click.echo("Collector name must not exceed 16 characters") - return False - - if port not in range(0, 65535 + 1): - click.echo("Collector port number must be between 0 and 65535") - return False - - import socket - try: - socket.inet_pton(socket.AF_INET, ip) - except: - try: - socket.inet_pton(socket.AF_INET6, ip) - except: - click.echo("Invalid collector IPv4 or IPv6 address") - return False - - return True - + config_db.mod_entry('SFLOW_COLLECTOR', name, None) # # 'sflow agent-id' group @@ -1568,7 +1561,7 @@ def add(ctx, ifname): return sflow_tbl['global']['agent_id'] = ifname - config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) + config_db.mod_entry('SFLOW', 'global', sflow_tbl['global']) # # 'sflow' command ('config sflow agent-id del') @@ -1588,7 +1581,7 @@ def delete(ctx): return sflow_tbl['global'].pop('agent_id') - config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) + config_db.mod_entry('SFLOW', 'global', sflow_tbl['global']) if __name__ == '__main__': diff --git a/show/main.py b/show/main.py index 8cd15a3598..ca7f7ba2bb 100755 --- a/show/main.py +++ b/show/main.py @@ -1806,63 +1806,37 @@ def sflow_interface(ctx): def sflow_appDB_connect(): db = SonicV2Connector(host='127.0.0.1') db.connect(db.APPL_DB, False) - keys = db.keys(db.APPL_DB, 'SFLOW_SAMPLE_RATE_TABLE:*') - if not keys: - click.echo('No sampling rate information found in apps_db') - return None - return db.get_all(db.APPL_DB, keys[0]) + return db def show_sflow_interface(config_db): - default_admin_state = 'enabled' - sflow_sampling_tbl = sflow_appDB_connect() - if not sflow_sampling_tbl: + sess_db = sflow_appDB_connect() + if not sess_db: click.echo("sflow AppDB error") return port_tbl = config_db.get_table('PORT') - idx_to_port_map = {int(port_tbl[name]['index']): name for name in - port_tbl.keys()} - sflow_session_tbl = config_db.get_table('SFLOW_SESSION') - all_session_admin_state = None - if sflow_session_tbl and 'all' in sflow_session_tbl.keys(): - if 'admin_state' in sflow_session_tbl['all']: - all_session_admin_state = sflow_session_tbl['all']['admin_state'] - if not port_tbl: click.echo("No ports configured") return + idx_to_port_map = {int(port_tbl[name]['index']): name for name in + port_tbl.keys()} click.echo("\nsFlow interface configurations") header = ['Interface', 'Admin State', 'Sampling Rate'] body = [] for idx in sorted(idx_to_port_map.keys()): pname = idx_to_port_map[idx] + intf_key = 'SFLOW_SESSION_TABLE:' + pname + sess_info = sess_db.get_all(sess_db.APPL_DB, intf_key) + if sess_info is None: + continue body_info = [pname] - - if sflow_session_tbl and pname in sflow_session_tbl.keys(): - if 'admin_state' in sflow_session_tbl[pname].keys(): - body_info.append(sflow_session_tbl[pname]['admin_state']) - elif all_session_admin_state is not None: - body_info.append(all_session_admin_state) - else: - body_info.append(default_admin_state) - - if 'sample_rate' in sflow_session_tbl[pname].keys(): - body_info.append(sflow_session_tbl[pname]['sample_rate']) - else: - body_info.append(sflow_sampling_tbl[port_tbl[pname]['speed']]) - else: - if all_session_admin_state is not None: - body_info.append(all_session_admin_state) - else: - body_info.append(default_admin_state) - body_info.append(sflow_sampling_tbl[port_tbl[pname]['speed']]) + body_info.append(sess_info['admin_state']) + body_info.append(sess_info['sample_rate']) body.append(body_info) click.echo(tabulate(body, header, tablefmt='grid')) def show_sflow_global(config_db): - default_polling = 20 - default_agent = 'default' sflow_info = config_db.get_table('SFLOW') global_admin_state = 'disabled' @@ -1870,20 +1844,20 @@ def show_sflow_global(config_db): global_admin_state = sflow_info['global']['admin_state'] click.echo("\nsFlow Global Information:") - click.echo(" sFlow Daemon State: {}".format(global_admin_state)) + click.echo(" sFlow Admin State: {}".format(global_admin_state)) click.echo(" SFlow Polling Interval: ", nl=False) if (sflow_info and 'polling_interval' in sflow_info['global'].keys()): - click.echo(" {}".format(sflow_info['global']['polling_interval'])) + click.echo(" {}".format(sflow_info['global']['polling_interval'])) else: - click.echo(" {}".format(default_polling)) + click.echo(" default") click.echo(" SFlow AgentID: ", nl=False) if (sflow_info and 'agent_id' in sflow_info['global'].keys()): - click.echo(" {}".format(sflow_info['global']['agent_id'])) + click.echo(" {}".format(sflow_info['global']['agent_id'])) else: - click.echo(" {}".format(default_agent)) + click.echo(" default") sflow_info = config_db.get_table('SFLOW_COLLECTOR') click.echo("\n {} Collectors configured:".format(len(sflow_info))) From 0db28682257218b4ba6f9f8aa456ce9896a931a5 Mon Sep 17 00:00:00 2001 From: Garrick He Date: Thu, 10 Oct 2019 15:58:20 -0700 Subject: [PATCH 13/15] fix: Minor change to deleting agent-id * Change to deleting agent-id logic Signed-off-by: Garrick He --- config/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/main.py b/config/main.py index 0169dfc2c9..d1507ded1b 100755 --- a/config/main.py +++ b/config/main.py @@ -1581,7 +1581,7 @@ def delete(ctx): return sflow_tbl['global'].pop('agent_id') - config_db.mod_entry('SFLOW', 'global', sflow_tbl['global']) + config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) if __name__ == '__main__': From d435dd2b3e3beb267cd0c3cdeb8eaf681e8e8d6e Mon Sep 17 00:00:00 2001 From: Garrick He Date: Thu, 17 Oct 2019 13:53:57 -0700 Subject: [PATCH 14/15] Change admin-state * Change admin-state from enable/disable to up/down to match SONiC YANG. Signed-off-by: Garrick He --- config/main.py | 24 ++++++++++++------------ show/main.py | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/config/main.py b/config/main.py index d1507ded1b..5a4615b3f7 100755 --- a/config/main.py +++ b/config/main.py @@ -1068,7 +1068,7 @@ def remove(ctx, interface_name, ip_addr): ctx.fail("'interface_name' is not valid. Valid names [Ethernet/PortChannel/Vlan/Loopback]") except ValueError: ctx.fail("'ip_addr' is not valid.") - + exists = False if if_table: interfaces = config_db.get_table(if_table) @@ -1338,9 +1338,9 @@ def enable(ctx): sflow_tbl = config_db.get_table('SFLOW') if not sflow_tbl: - sflow_tbl = {'global': {'admin_state': 'enable'}} + sflow_tbl = {'global': {'admin_state': 'up'}} else: - sflow_tbl['global']['admin_state'] = 'enable' + sflow_tbl['global']['admin_state'] = 'up' config_db.mod_entry('SFLOW', 'global', sflow_tbl['global']) @@ -1355,9 +1355,9 @@ def disable(ctx): sflow_tbl = config_db.get_table('SFLOW') if not sflow_tbl: - sflow_tbl = {'global': {'admin_state': 'disable'}} + sflow_tbl = {'global': {'admin_state': 'down'}} else: - sflow_tbl['global']['admin_state'] = 'disable' + sflow_tbl['global']['admin_state'] = 'down' config_db.mod_entry('SFLOW', 'global', sflow_tbl['global']) @@ -1377,7 +1377,7 @@ def polling_int(ctx, interval): sflow_tbl = config_db.get_table('SFLOW') if not sflow_tbl: - sflow_tbl = {'global': {'admin_state': 'disable'}} + sflow_tbl = {'global': {'admin_state': 'down'}} sflow_tbl['global']['polling_interval'] = interval config_db.mod_entry('SFLOW', 'global', sflow_tbl['global']) @@ -1410,10 +1410,10 @@ def enable(ctx, ifname): intf_dict = config_db.get_table('SFLOW_SESSION') if intf_dict and ifname in intf_dict.keys(): - intf_dict[ifname]['admin_state'] = 'enable' + intf_dict[ifname]['admin_state'] = 'up' config_db.mod_entry('SFLOW_SESSION', ifname, intf_dict[ifname]) else: - config_db.mod_entry('SFLOW_SESSION', ifname, {'admin_state': 'enable'}) + config_db.mod_entry('SFLOW_SESSION', ifname, {'admin_state': 'up'}) # # 'sflow' command ('config sflow interface disable ...') @@ -1430,11 +1430,11 @@ def disable(ctx, ifname): intf_dict = config_db.get_table('SFLOW_SESSION') if intf_dict and ifname in intf_dict.keys(): - intf_dict[ifname]['admin_state'] = 'disable' + intf_dict[ifname]['admin_state'] = 'down' config_db.mod_entry('SFLOW_SESSION', ifname, intf_dict[ifname]) else: config_db.mod_entry('SFLOW_SESSION', ifname, - {'admin_state': 'disable'}) + {'admin_state': 'down'}) # # 'sflow' command ('config sflow interface sample-rate ...') @@ -1554,7 +1554,7 @@ def add(ctx, ifname): sflow_tbl = config_db.get_table('SFLOW') if not sflow_tbl: - sflow_tbl = {'global': {'admin_state': 'disable'}} + sflow_tbl = {'global': {'admin_state': 'down'}} if 'agent_id' in sflow_tbl['global'].keys(): click.echo("Agent already configured. Please delete it first.") @@ -1574,7 +1574,7 @@ def delete(ctx): sflow_tbl = config_db.get_table('SFLOW') if not sflow_tbl: - sflow_tbl = {'global': {'admin_state': 'disable'}} + sflow_tbl = {'global': {'admin_state': 'down'}} if 'agent_id' not in sflow_tbl['global'].keys(): click.echo("sFlow agent not configured.") diff --git a/show/main.py b/show/main.py index ca7f7ba2bb..350a5bcd80 100755 --- a/show/main.py +++ b/show/main.py @@ -1839,7 +1839,7 @@ def show_sflow_interface(config_db): def show_sflow_global(config_db): sflow_info = config_db.get_table('SFLOW') - global_admin_state = 'disabled' + global_admin_state = 'down' if sflow_info: global_admin_state = sflow_info['global']['admin_state'] From 21706ac68f4df6b00e58a84c240601380313cbe2 Mon Sep 17 00:00:00 2001 From: Garrick He Date: Tue, 29 Oct 2019 21:26:49 -0700 Subject: [PATCH 15/15] Fix CLI output alignment * Fix some minor alignment issues in the show sFlow global output. Signed-off-by: Garrick He --- show/main.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/show/main.py b/show/main.py index 350a5bcd80..275bfd18d2 100755 --- a/show/main.py +++ b/show/main.py @@ -1844,28 +1844,27 @@ def show_sflow_global(config_db): global_admin_state = sflow_info['global']['admin_state'] click.echo("\nsFlow Global Information:") - click.echo(" sFlow Admin State: {}".format(global_admin_state)) + click.echo(" sFlow Admin State:".ljust(30) + "{}".format(global_admin_state)) - click.echo(" SFlow Polling Interval: ", nl=False) + click.echo(" sFlow Polling Interval:".ljust(30), nl=False) if (sflow_info and 'polling_interval' in sflow_info['global'].keys()): - click.echo(" {}".format(sflow_info['global']['polling_interval'])) + click.echo("{}".format(sflow_info['global']['polling_interval'])) else: - click.echo(" default") + click.echo("default") - click.echo(" SFlow AgentID: ", nl=False) + click.echo(" sFlow AgentID:".ljust(30), nl=False) if (sflow_info and 'agent_id' in sflow_info['global'].keys()): - click.echo(" {}".format(sflow_info['global']['agent_id'])) + click.echo("{}".format(sflow_info['global']['agent_id'])) else: - click.echo(" default") + click.echo("default") sflow_info = config_db.get_table('SFLOW_COLLECTOR') click.echo("\n {} Collectors configured:".format(len(sflow_info))) for collector_name in sorted(sflow_info.keys()): - click.echo(" Name: {}\t IP addr: {}\t UDP port:{}".format( - collector_name, - sflow_info[collector_name]['collector_ip'], - sflow_info[collector_name]['collector_port'])) + click.echo(" Name: {}".format(collector_name).ljust(30) + + "IP addr: {}".format(sflow_info[collector_name]['collector_ip']).ljust(20) + + "UDP port: {}".format(sflow_info[collector_name]['collector_port'])) #