-
Notifications
You must be signed in to change notification settings - Fork 664
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
sonic-utilities: Add support for sFlow #592
Changes from 12 commits
8913251
6ba881f
d00dddd
0a268af
bb54a87
ad4a0af
54a1f80
b5fcb07
a78643e
7e04e74
5e316ec
b2e555d
6c25969
0db2868
d435dd2
21706ac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
import netaddr | ||
import re | ||
import syslog | ||
import netifaces | ||
|
||
import sonic_device_util | ||
import ipaddress | ||
|
@@ -312,6 +313,7 @@ def _restart_services(): | |
'pmon', | ||
'lldp', | ||
'hostcfgd', | ||
'sflow', | ||
] | ||
for service in services: | ||
try: | ||
|
@@ -1313,5 +1315,281 @@ def naming_mode_alias(): | |
set_interface_naming_mode('alias') | ||
|
||
|
||
# | ||
# 'sflow' group ('config sflow ...') | ||
# | ||
@config.group() | ||
@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() | ||
@click.pass_context | ||
def enable(ctx): | ||
"""Enable sFlow""" | ||
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']) | ||
|
||
# | ||
# 'sflow' command ('config sflow disable') | ||
# | ||
@sflow.command() | ||
@click.pass_context | ||
def disable(ctx): | ||
"""Disable sFlow""" | ||
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']) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will change. |
||
|
||
# | ||
# 'sflow' command ('config sflow polling-interval ...') | ||
# | ||
@sflow.command('polling-interval') | ||
@click.argument('interval', metavar='<polling_interval>', required=True, | ||
type=int) | ||
@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: | ||
click.echo("Polling interval must be between 5-300 (0 to disable)") | ||
|
||
config_db = ctx.obj['db'] | ||
sflow_tbl = config_db.get_table('SFLOW') | ||
|
||
if not sflow_tbl: | ||
sflow_tbl = {'global': {'admin_state': 'disable'}} | ||
|
||
sflow_tbl['global']['polling_interval'] = interval | ||
config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mod_entry ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will change. |
||
|
||
def is_valid_sample_rate(rate): | ||
return rate in range(256, 8388608 + 1) | ||
|
||
|
||
# | ||
# 'sflow interface' group | ||
# | ||
@sflow.group() | ||
@click.pass_context | ||
def interface(ctx): | ||
"""Configure sFlow settings for an interface""" | ||
pass | ||
|
||
# | ||
# 'sflow' command ('config sflow interface enable ...') | ||
# | ||
@interface.command() | ||
@click.argument('ifname', metavar='<interface_name>', 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 = ctx.obj['db'] | ||
intf_dict = config_db.get_table('SFLOW_SESSION') | ||
|
||
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('ifname', metavar='<interface_name>', 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 = ctx.obj['db'] | ||
intf_dict = config_db.get_table('SFLOW_SESSION') | ||
|
||
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', ifname, | ||
{'admin_state': 'disable'}) | ||
|
||
# | ||
# 'sflow' command ('config sflow interface sample-rate ...') | ||
# | ||
@interface.command('sample-rate') | ||
@click.argument('ifname', metavar='<interface_name>', required=True, type=str) | ||
@click.argument('rate', metavar='<sample_rate>', required=True, type=int) | ||
@click.pass_context | ||
def sample_rate(ctx, ifname, rate): | ||
if not interface_name_is_valid(ifname) and ifname != 'all': | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to include key There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This check is to ensure the user gives a valid interface name or |
||
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 = ctx.obj['db'] | ||
sess_dict = config_db.get_table('SFLOW_SESSION') | ||
|
||
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', ifname, {'sample_rate': rate}) | ||
|
||
|
||
# | ||
# 'sflow collector' group | ||
# | ||
@sflow.group() | ||
@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='<collector_name>', required=True) | ||
@click.argument('ipaddr', metavar='<IPv4/v6_address>', 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): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you see an issue with function being defined after invocation? I see that this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Didn't notice a problem, but will move it up. |
||
return | ||
|
||
config_db = ctx.obj['db'] | ||
collector_tbl = config_db.get_table('SFLOW_COLLECTOR') | ||
|
||
if (collector_tbl and name not in collector_tbl.keys() and len(collector_tbl) == 2): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Collectors are limited to 2. Is this a limit from upper-layer hsflowd daemon? Also confused about the output in the PR description that you were able to configure 4 collectors. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because you were reading the output from an outdated version of the code. I changed that afterwards:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this a limitation from upper-layer hsflowd daemon? |
||
click.echo("Only 2 collectors can be configured, please delete one") | ||
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', metavar='<collector_name>', required=True) | ||
@click.pass_context | ||
def del_collector(ctx, name): | ||
"""Delete a sFlow collector""" | ||
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) > 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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will change. |
||
except: | ||
try: | ||
socket.inet_pton(socket.AF_INET6, ip) | ||
except: | ||
click.echo("Invalid collector IPv4 or IPv6 address") | ||
return False | ||
|
||
return True | ||
|
||
|
||
# | ||
# 'sflow agent-id' group | ||
# | ||
@sflow.group('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('ifname', metavar='<interface_name>', required=True) | ||
@click.pass_context | ||
def add(ctx, ifname): | ||
"""Add sFlow agent information""" | ||
if ifname not in netifaces.interfaces(): | ||
click.echo("Invalid interface name") | ||
return | ||
|
||
config_db = ctx.obj['db'] | ||
sflow_tbl = config_db.get_table('SFLOW') | ||
|
||
if not sflow_tbl: | ||
sflow_tbl = {'global': {'admin_state': 'disable'}} | ||
|
||
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']) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you can use mod_entry here as well |
||
|
||
# | ||
# 'sflow' command ('config sflow agent-id del') | ||
# | ||
@agent_id.command('del') | ||
@click.pass_context | ||
def delete(ctx): | ||
"""Delete sFlow agent information""" | ||
config_db = ctx.obj['db'] | ||
sflow_tbl = config_db.get_table('SFLOW') | ||
|
||
if not sflow_tbl: | ||
sflow_tbl = {'global': {'admin_state': 'disable'}} | ||
|
||
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']) | ||
|
||
|
||
if __name__ == '__main__': | ||
config() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could just do a
config_db.mod_entry
for setting theadmin_state
fieldThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will change.