Skip to content
This repository was archived by the owner on Aug 15, 2025. It is now read-only.

Commit 5631aed

Browse files
VPP: T7175: Add sFlow conf mode CLI and startup template (#32)
* VPP: T7175 Added conf mode CLI for VPP sflow plugin and updated VPP template to include plugin. * VPP: T7175: Conf mode CLI and startup template. * T7175: VPP fix sFlow verify use vpp enstead of enable_vpp * T7175: VPP add sFlow smoketest * T7175: VPP remove unused config_changed variable --------- Co-authored-by: Viacheslav <v.gletenko@vyos.io>
1 parent d37cca0 commit 5631aed

6 files changed

Lines changed: 206 additions & 1 deletion

File tree

data/config-mode-dependencies/vyos-vpp.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
"vpp_acl": ["vpp_acl"],
1414
"vpp_nat": ["vpp_nat"],
1515
"vpp_nat_cgnat": ["vpp_nat_cgnat"],
16-
"vpp_kernel_interface": ["vpp_kernel-interfaces"]
16+
"vpp_kernel_interface": ["vpp_kernel-interfaces"],
17+
"vpp_sflow": ["vpp_sflow"]
1718
},
1819
"vpp_interfaces_bonding": {
1920
"vpp_interfaces_xconnect": ["vpp_interfaces_xconnect"],

data/templates/vpp/startup.conf.j2

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ plugins {
9292
plugin linux_cp_plugin.so { enable }
9393
plugin linux_nl_plugin.so { enable }
9494
plugin pppoe_plugin.so { enable }
95+
plugin sflow_plugin.so { enable }
9596
# NAT uncomment if needed
9697
# plugin cnat_plugin.so { enable }
9798
plugin nat_plugin.so { enable }

interface-definitions/vpp.xml.in

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,36 @@
936936
</node>
937937
</children>
938938
</node>
939+
<node name="sflow" owner="${vyos_conf_scripts_dir}/vpp_sflow.py">
940+
<properties>
941+
<help>VPP data-plane sFlow</help>
942+
<priority>322</priority>
943+
</properties>
944+
<children>
945+
<leafNode name="interface">
946+
<properties>
947+
<help>Interface name</help>
948+
<completionHelp>
949+
<script>${vyos_completion_dir}/list_interfaces</script>
950+
</completionHelp>
951+
<valueHelp>
952+
<format>txt</format>
953+
<description>Interface name</description>
954+
</valueHelp>
955+
<multi/>
956+
</properties>
957+
</leafNode>
958+
<leafNode name="sample-rate">
959+
<properties>
960+
<help>sFlow sample rate</help>
961+
<valueHelp>
962+
<format>u32</format>
963+
<description>sFlow sample rate</description>
964+
</valueHelp>
965+
</properties>
966+
</leafNode>
967+
</children>
968+
</node>
939969
<node name="nat">
940970
<properties>
941971
<help>Network Address Translation (NAT) settings</help>

smoketest/scripts/cli/test_vpp.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1393,6 +1393,30 @@ def test_17_vpp_nat(self):
13931393
_, out = rc_cmd('sudo vppctl show nat44 summary')
13941394
self.assertIn(f'max translations per thread: {sess_limit} fib 0', out)
13951395

1396+
def test_18_vpp_sflow(self):
1397+
base_sflow = ['system', 'sflow']
1398+
1399+
self.cli_set(base_path + ['sflow', 'interface', interface])
1400+
self.cli_set(base_sflow + ['interface', interface])
1401+
self.cli_set(base_sflow + ['server', '127.0.0.1'])
1402+
self.cli_set(base_sflow + ['vpp'])
1403+
self.cli_commit()
1404+
1405+
# Check sFlow
1406+
_, out = rc_cmd('sudo vppctl show sflow')
1407+
1408+
expected_entries = (
1409+
'sflow sampling-direction ingress',
1410+
f'sflow enable {interface}',
1411+
'interfaces enabled: 1',
1412+
)
1413+
1414+
for expected_entry in expected_entries:
1415+
self.assertIn(expected_entry, out)
1416+
1417+
self.cli_delete(base_sflow)
1418+
self.cli_commit()
1419+
13961420

13971421
if __name__ == '__main__':
13981422
unittest.main(verbosity=2)

src/conf_mode/vpp.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ def get_config(config=None):
149149
if conf.exists(['vpp', 'nat', 'cgnat']):
150150
set_dependents('vpp_nat_cgnat', conf)
151151

152+
# sFlow dependency
153+
if conf.exists(['vpp', 'sflow']):
154+
set_dependents('vpp_sflow', conf)
155+
152156
# ACL dependency
153157
if conf.exists(['vpp', 'acl']):
154158
set_dependents('vpp_acl', conf)

src/conf_mode/vpp_sflow.py

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# Copyright (C) 2025 VyOS maintainers and contributors
2+
#
3+
# This program is free software; you can redistribute it and/or modify
4+
# it under the terms of the GNU General Public License version 2 or later as
5+
# published by the Free Software Foundation.
6+
#
7+
# This program is distributed in the hope that it will be useful,
8+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
# GNU General Public License for more details.
11+
#
12+
# You should have received a copy of the GNU General Public License
13+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
14+
#
15+
16+
from vyos import ConfigError
17+
from vyos.config import Config
18+
from vyos.vpp.utils import cli_ifaces_list
19+
from vyos.vpp import VPPControl
20+
21+
22+
def get_config(config=None) -> dict:
23+
if config:
24+
conf = config
25+
else:
26+
conf = Config()
27+
28+
base = ['vpp', 'sflow']
29+
30+
# Get config_dict with default values
31+
config = conf.get_config_dict(
32+
base,
33+
key_mangling=('-', '_'),
34+
get_first_key=True,
35+
no_tag_node_value_mangle=True,
36+
with_defaults=True,
37+
with_recursive_defaults=True,
38+
)
39+
40+
# Get effective config as we need full dictionary for deletion
41+
effective_config = conf.get_config_dict(
42+
base,
43+
key_mangling=('-', '_'),
44+
effective=True,
45+
get_first_key=True,
46+
no_tag_node_value_mangle=True,
47+
)
48+
49+
# Get system sflow configuration to check for server
50+
system_sflow = conf.get_config_dict(
51+
['system', 'sflow'],
52+
key_mangling=('-', '_'),
53+
get_first_key=True,
54+
no_tag_node_value_mangle=True,
55+
)
56+
57+
if system_sflow:
58+
config['system_sflow'] = system_sflow
59+
60+
if not config:
61+
config['remove'] = True
62+
return config
63+
64+
# Add list of VPP interfaces to the config
65+
config.update({'vpp_ifaces': cli_ifaces_list(conf)})
66+
67+
if effective_config:
68+
config.update({'effective': effective_config})
69+
70+
return config
71+
72+
73+
def verify(config):
74+
if 'remove' in config:
75+
return None
76+
77+
# Check if interface section exists
78+
if 'interface' not in config:
79+
return None
80+
81+
# Verify that all interfaces specified exist in VPP
82+
for interface in config['interface']:
83+
if interface not in config['vpp_ifaces']:
84+
raise ConfigError(
85+
f'{interface} must be a VPP interface for sFlow monitoring'
86+
)
87+
88+
# Verify sample rate is a positive integer
89+
if 'sample_rate' in config:
90+
try:
91+
sample_rate = int(config['sample_rate'])
92+
if sample_rate <= 0:
93+
raise ConfigError('sFlow sample rate must be a positive integer')
94+
except ValueError:
95+
raise ConfigError('sFlow sample rate must be a valid integer')
96+
97+
# Verify that system sflow has enable-vpp defined
98+
if 'system_sflow' not in config or 'vpp' not in config.get('system_sflow', {}):
99+
raise ConfigError(
100+
'sFlow enable-vpp must be defined under system sflow configuration'
101+
)
102+
103+
104+
def generate(config):
105+
# No templates to render for sFlow
106+
pass
107+
108+
109+
def apply(config):
110+
# Initialize VPP control API
111+
vpp = VPPControl(attempts=20, interval=500)
112+
113+
if 'remove' in config:
114+
# Disable sFlow on all interfaces
115+
for interface in config.get('effective', {}).get('interface', []):
116+
vpp.cli_cmd(f'sflow enable-disable {interface} disable')
117+
return None
118+
119+
# Configure sample rate if specified
120+
if 'sample_rate' in config:
121+
vpp.cli_cmd(f'sflow sampling-rate {config["sample_rate"]}')
122+
123+
# Configure interfaces
124+
if 'interface' in config:
125+
# Enable sFlow on specified interfaces
126+
for interface in config['interface']:
127+
vpp.cli_cmd(f'sflow enable-disable {interface}')
128+
129+
# Disable sFlow on interfaces that were removed from config
130+
effective_interfaces = config.get('effective', {}).get('interface', [])
131+
if effective_interfaces:
132+
for interface in effective_interfaces:
133+
if interface not in config['interface']:
134+
vpp.cli_cmd(f'sflow enable-disable {interface} disable')
135+
136+
137+
if __name__ == '__main__':
138+
try:
139+
c = get_config()
140+
verify(c)
141+
generate(c)
142+
apply(c)
143+
except ConfigError as e:
144+
print(e)
145+
exit(1)

0 commit comments

Comments
 (0)