Skip to content

Commit 6d5017a

Browse files
authored
Merge branch 'master' into acl_port_issue
2 parents 0b896c6 + a73f156 commit 6d5017a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+5704
-284
lines changed

clear/main.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import sys
55
import click
66
import utilities_common.cli as clicommon
7+
import utilities_common.multi_asic as multi_asic_util
78

9+
from flow_counter_util.route import exit_if_route_flow_counter_not_support
810
from utilities_common import util_base
911
from show.plugins.pbh import read_pbh_counters
1012
from config.plugins.pbh import serialize_pbh_counters
@@ -484,6 +486,53 @@ def flowcnt_trap():
484486
run_command(command)
485487

486488

489+
# ("sonic-clear flowcnt-route")
490+
@cli.group(invoke_without_command=True)
491+
@click.option('--namespace', '-n', 'namespace', default=None, type=click.Choice(multi_asic_util.multi_asic_ns_choices()), show_default=True, help='Namespace name or all')
492+
@click.pass_context
493+
def flowcnt_route(ctx, namespace):
494+
"""Clear all route flow counters"""
495+
exit_if_route_flow_counter_not_support()
496+
if ctx.invoked_subcommand is None:
497+
command = "flow_counters_stat -c -t route"
498+
# None namespace means default namespace
499+
if namespace is not None:
500+
command += " -n {}".format(namespace)
501+
clicommon.run_command(command)
502+
503+
504+
# ("sonic-clear flowcnt-route pattern")
505+
@flowcnt_route.command()
506+
@click.option('--vrf', help='VRF/VNET name or default VRF')
507+
@click.option('--namespace', '-n', 'namespace', default=None, type=click.Choice(multi_asic_util.multi_asic_ns_choices()), show_default=True, help='Namespace name or all')
508+
@click.argument('prefix-pattern', required=True)
509+
def pattern(prefix_pattern, vrf, namespace):
510+
"""Clear route flow counters by pattern"""
511+
command = "flow_counters_stat -c -t route --prefix_pattern {}".format(prefix_pattern)
512+
if vrf:
513+
command += ' --vrf {}'.format(vrf)
514+
# None namespace means default namespace
515+
if namespace is not None:
516+
command += " -n {}".format(namespace)
517+
clicommon.run_command(command)
518+
519+
520+
# ("sonic-clear flowcnt-route route")
521+
@flowcnt_route.command()
522+
@click.option('--vrf', help='VRF/VNET name or default VRF')
523+
@click.option('--namespace', '-n', 'namespace', default=None, type=click.Choice(multi_asic_util.multi_asic_ns_choices()), show_default=True, help='Namespace name or all')
524+
@click.argument('prefix', required=True)
525+
def route(prefix, vrf, namespace):
526+
"""Clear route flow counters by prefix"""
527+
command = "flow_counters_stat -c -t route --prefix {}".format(prefix)
528+
if vrf:
529+
command += ' --vrf {}'.format(vrf)
530+
# None namespace means default namespace
531+
if namespace is not None:
532+
command += " -n {}".format(namespace)
533+
clicommon.run_command(command)
534+
535+
487536
# Load plugins and register them
488537
helper = util_base.UtilHelper()
489538
helper.load_and_register_plugins(plugins, cli)

config/flow_counters.py

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import click
2+
import ipaddress
3+
4+
from flow_counter_util.route import FLOW_COUNTER_ROUTE_PATTERN_TABLE, FLOW_COUNTER_ROUTE_MAX_MATCH_FIELD, DEFAULT_VRF, PATTERN_SEPARATOR
5+
from flow_counter_util.route import build_route_pattern, extract_route_pattern, exit_if_route_flow_counter_not_support
6+
from utilities_common.cli import AbbreviationGroup, pass_db
7+
from utilities_common import cli # To make mock work in unit test
8+
9+
#
10+
# 'flowcnt-route' group ('config flowcnt-route ...')
11+
#
12+
13+
14+
@click.group(cls=AbbreviationGroup, invoke_without_command=False)
15+
def flowcnt_route():
16+
"""Route flow counter related configuration tasks"""
17+
pass
18+
19+
20+
@flowcnt_route.group()
21+
def pattern():
22+
"""Set pattern for route flow counter"""
23+
pass
24+
25+
26+
@pattern.command(name='add')
27+
@click.option('-y', '--yes', is_flag=True)
28+
@click.option('--vrf', help='VRF/VNET name or default VRF')
29+
@click.option('--max', 'max_allowed_match', type=click.IntRange(1, 50), default=30, show_default=True, help='Max allowed match count')
30+
@click.argument('prefix-pattern', required=True)
31+
@pass_db
32+
def pattern_add(db, yes, vrf, max_allowed_match, prefix_pattern):
33+
"""Add pattern for route flow counter"""
34+
_update_route_flow_counter_config(db, vrf, max_allowed_match, prefix_pattern, True, yes)
35+
36+
37+
@pattern.command(name='remove')
38+
@click.option('--vrf', help='VRF/VNET name or default VRF')
39+
@click.argument('prefix-pattern', required=True)
40+
@pass_db
41+
def pattern_remove(db, vrf, prefix_pattern):
42+
"""Remove pattern for route flow counter"""
43+
_update_route_flow_counter_config(db, vrf, None, prefix_pattern, False)
44+
45+
46+
def _update_route_flow_counter_config(db, vrf, max_allowed_match, prefix_pattern, add, yes=False):
47+
"""
48+
Update route flow counter config
49+
:param db: db object
50+
:param vrf: vrf string, empty vrf will be treated as default vrf
51+
:param max_allowed_match: max allowed match count, $FLOW_COUNTER_ROUTE_MAX_MATCH_FIELD will be used if not specified
52+
:param prefix_pattern: route prefix pattern, automatically add prefix length if not specified
53+
:param add: True to add/set the configuration, otherwise remove
54+
:param yes: Don't ask question if True
55+
:return:
56+
"""
57+
exit_if_route_flow_counter_not_support()
58+
59+
if add:
60+
try:
61+
net = ipaddress.ip_network(prefix_pattern, strict=False)
62+
except ValueError as e:
63+
click.echo('Invalid prefix pattern: {}'.format(prefix_pattern))
64+
exit(1)
65+
66+
if '/' not in prefix_pattern:
67+
prefix_pattern += '/' + str(net.prefixlen)
68+
69+
key = build_route_pattern(vrf, prefix_pattern)
70+
for _, cfgdb in db.cfgdb_clients.items():
71+
if _try_find_existing_pattern_by_ip_type(cfgdb, net, key, yes):
72+
entry_data = cfgdb.get_entry(FLOW_COUNTER_ROUTE_PATTERN_TABLE, key)
73+
old_max_allowed_match = entry_data.get(FLOW_COUNTER_ROUTE_MAX_MATCH_FIELD)
74+
if old_max_allowed_match is not None and int(old_max_allowed_match) == max_allowed_match:
75+
click.echo('The route pattern already exists, nothing to be changed')
76+
exit(1)
77+
cfgdb.mod_entry(FLOW_COUNTER_ROUTE_PATTERN_TABLE,
78+
key,
79+
{FLOW_COUNTER_ROUTE_MAX_MATCH_FIELD: str(max_allowed_match)})
80+
else:
81+
found = False
82+
key = build_route_pattern(vrf, prefix_pattern)
83+
for _, cfgdb in db.cfgdb_clients.items():
84+
pattern_table = cfgdb.get_table(FLOW_COUNTER_ROUTE_PATTERN_TABLE)
85+
86+
for existing_key in pattern_table:
87+
exist_vrf, existing_prefix = extract_route_pattern(existing_key)
88+
if (exist_vrf == vrf or (vrf is None and exist_vrf == DEFAULT_VRF)) and existing_prefix == prefix_pattern:
89+
found = True
90+
cfgdb.set_entry(FLOW_COUNTER_ROUTE_PATTERN_TABLE, key, None)
91+
if not found:
92+
click.echo("Failed to remove route pattern: {} does not exist".format(key))
93+
exit(1)
94+
95+
96+
def _try_find_existing_pattern_by_ip_type(cfgdb, input_net, input_key, yes):
97+
"""Try to find the same IP type pattern from CONFIG DB.
98+
1. If found a pattern with the same IP type, but the patter does not equal, ask user if need to replace the old with new one
99+
a. If user types "yes", remove the old one, return False
100+
b. If user types "no", exit
101+
2. If found a pattern with the same IP type and the pattern equal, return True
102+
3. If not found a pattern with the same IP type, return False
103+
104+
Args:
105+
cfgdb (object): CONFIG DB object
106+
input_net (object): Input ip_network object
107+
input_key (str): Input key
108+
yes (bool): Whether ask user question
109+
110+
Returns:
111+
bool: True if found the same pattern in CONFIG DB
112+
"""
113+
input_type = type(input_net) # IPv4 or IPv6
114+
found_invalid = []
115+
found = None
116+
pattern_table = cfgdb.get_table(FLOW_COUNTER_ROUTE_PATTERN_TABLE)
117+
for existing_key in pattern_table:
118+
if isinstance(existing_key, tuple):
119+
existing_prefix = existing_key[1]
120+
existing_key = PATTERN_SEPARATOR.join(existing_key)
121+
else:
122+
_, existing_prefix = extract_route_pattern(existing_key)
123+
124+
# In case user configures an invalid pattern via CONFIG DB.
125+
if not existing_prefix: # Invalid pattern such as: "vrf1|"
126+
click.echo('Detect invalid route pattern in existing configuration {}'.format(existing_key))
127+
found_invalid.append(existing_key)
128+
continue
129+
130+
try:
131+
existing_net = ipaddress.ip_network(existing_prefix, strict=False)
132+
except ValueError as e: # Invalid pattern such as: "vrf1|invalid"
133+
click.echo('Detect invalid route pattern in existing configuration {}'.format(existing_key))
134+
found_invalid.append(existing_key)
135+
continue
136+
137+
if type(existing_net) == input_type:
138+
found = existing_key
139+
break
140+
141+
if found == input_key:
142+
return True
143+
144+
if not found and found_invalid:
145+
# If not found but there is an invalid one, ask user to replace the invalid one
146+
found = found_invalid[0]
147+
148+
if found:
149+
if not yes:
150+
answer = cli.query_yes_no('Only support 1 IPv4 route pattern and 1 IPv6 route pattern, remove existing pattern {}?'.format(found))
151+
else:
152+
answer = True
153+
if answer:
154+
click.echo('Replacing existing route pattern {} with {}'.format(existing_key, input_key))
155+
cfgdb.set_entry(FLOW_COUNTER_ROUTE_PATTERN_TABLE, existing_key, None)
156+
else:
157+
exit(0)
158+
return False

0 commit comments

Comments
 (0)