Skip to content
This repository was archived by the owner on Aug 15, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion data/config-mode-dependencies/vyos-vpp.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"vpp_acl": ["vpp_acl"],
"vpp_nat": ["vpp_nat"],
"vpp_nat_cgnat": ["vpp_nat_cgnat"],
"vpp_kernel_interface": ["vpp_kernel-interfaces"]
"vpp_kernel_interface": ["vpp_kernel-interfaces"],
"vpp_sflow": ["vpp_sflow"]
},
"vpp_interfaces_bonding": {
"vpp_interfaces_xconnect": ["vpp_interfaces_xconnect"],
Expand Down
1 change: 1 addition & 0 deletions data/templates/vpp/startup.conf.j2
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ plugins {
plugin linux_cp_plugin.so { enable }
plugin linux_nl_plugin.so { enable }
plugin pppoe_plugin.so { enable }
plugin sflow_plugin.so { enable }
# NAT uncomment if needed
# plugin cnat_plugin.so { enable }
plugin nat_plugin.so { enable }
Expand Down
30 changes: 30 additions & 0 deletions interface-definitions/vpp.xml.in
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,36 @@
</node>
</children>
</node>
<node name="sflow" owner="${vyos_conf_scripts_dir}/vpp_sflow.py">
Comment thread
sever-sever marked this conversation as resolved.
<properties>
<help>VPP data-plane sFlow</help>
<priority>322</priority>
</properties>
<children>
<leafNode name="interface">
<properties>
<help>Interface name</help>
<completionHelp>
<script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<valueHelp>
<format>txt</format>
<description>Interface name</description>
</valueHelp>
<multi/>
</properties>
</leafNode>
<leafNode name="sample-rate">
<properties>
<help>sFlow sample rate</help>
Comment thread
sever-sever marked this conversation as resolved.
<valueHelp>
<format>u32</format>
<description>sFlow sample rate</description>
</valueHelp>
</properties>
</leafNode>
</children>
</node>
<node name="nat">
<properties>
<help>Network Address Translation (NAT) settings</help>
Expand Down
24 changes: 24 additions & 0 deletions smoketest/scripts/cli/test_vpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -1393,6 +1393,30 @@ def test_17_vpp_nat(self):
_, out = rc_cmd('sudo vppctl show nat44 summary')
self.assertIn(f'max translations per thread: {sess_limit} fib 0', out)

def test_18_vpp_sflow(self):
base_sflow = ['system', 'sflow']

self.cli_set(base_path + ['sflow', 'interface', interface])
self.cli_set(base_sflow + ['interface', interface])
self.cli_set(base_sflow + ['server', '127.0.0.1'])
self.cli_set(base_sflow + ['vpp'])
self.cli_commit()

# Check sFlow
_, out = rc_cmd('sudo vppctl show sflow')

expected_entries = (
'sflow sampling-direction ingress',
f'sflow enable {interface}',
'interfaces enabled: 1',
)

for expected_entry in expected_entries:
self.assertIn(expected_entry, out)

self.cli_delete(base_sflow)
self.cli_commit()


if __name__ == '__main__':
unittest.main(verbosity=2)
4 changes: 4 additions & 0 deletions src/conf_mode/vpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ def get_config(config=None):
if conf.exists(['vpp', 'nat', 'cgnat']):
set_dependents('vpp_nat_cgnat', conf)

# sFlow dependency
if conf.exists(['vpp', 'sflow']):
set_dependents('vpp_sflow', conf)

# ACL dependency
if conf.exists(['vpp', 'acl']):
set_dependents('vpp_acl', conf)
Expand Down
145 changes: 145 additions & 0 deletions src/conf_mode/vpp_sflow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# Copyright (C) 2025 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#

from vyos import ConfigError
from vyos.config import Config
from vyos.vpp.utils import cli_ifaces_list
from vyos.vpp import VPPControl


def get_config(config=None) -> dict:
if config:
conf = config
else:
conf = Config()

base = ['vpp', 'sflow']

# Get config_dict with default values
config = conf.get_config_dict(
base,
key_mangling=('-', '_'),
get_first_key=True,
no_tag_node_value_mangle=True,
with_defaults=True,
with_recursive_defaults=True,
)

# Get effective config as we need full dictionary for deletion
effective_config = conf.get_config_dict(
base,
key_mangling=('-', '_'),
effective=True,
get_first_key=True,
no_tag_node_value_mangle=True,
)

# Get system sflow configuration to check for server
system_sflow = conf.get_config_dict(
['system', 'sflow'],
key_mangling=('-', '_'),
get_first_key=True,
no_tag_node_value_mangle=True,
)

if system_sflow:
config['system_sflow'] = system_sflow

if not config:
config['remove'] = True
return config

# Add list of VPP interfaces to the config
config.update({'vpp_ifaces': cli_ifaces_list(conf)})

if effective_config:
config.update({'effective': effective_config})

return config


def verify(config):
if 'remove' in config:
return None

# Check if interface section exists
if 'interface' not in config:
return None

# Verify that all interfaces specified exist in VPP
for interface in config['interface']:
if interface not in config['vpp_ifaces']:

@sever-sever sever-sever Jul 29, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ServerForge do you know if subinterfaces are also allowed for this plugin?
For example

set interfaces ethernet eth1 address '192.0.2.1/30'
set interfaces ethernet eth1 description 'LAN'
set interfaces ethernet eth1 vif 11 address '10.0.11.1/30'
set interfaces ethernet eth1 vif 12 address '10.0.12.1/30'
set interfaces ethernet eth1 vif 13 address '10.0.13.1/30'
set interfaces ethernet eth1 vif 14 address '10.0.14.1/30'
set interfaces ethernet eth1 vif 15 address '10.0.15.1/30'
set interfaces ethernet eth1 vif 16 address '10.0.16.1/30'
set interfaces ethernet eth1 vif 17 address '10.0.17.1/30'
set interfaces ethernet eth1 vif 18 address '10.0.18.1/30'
set interfaces ethernet eth1 vif 19 address '10.0.19.1/30'
set interfaces ethernet eth1 vif 20 address '10.0.20.1/30'
set interfaces ethernet eth1 vif 21 address '10.0.21.1/30'
set interfaces ethernet eth1 vif 22 address '10.0.22.1/30'
set interfaces ethernet eth1 vif 23 address '10.0.23.1/30'
set interfaces ethernet eth1 vif 24 address '10.0.24.1/30'
set interfaces ethernet eth1 vif 25 address '10.0.25.1/30'
set interfaces ethernet eth1 vif 26 address '10.0.26.1/30'
set system option kernel cpu disable-nmi-watchdog
set system option kernel cpu isolate-cpus '1,2,4-5'
set system option kernel cpu nohz-full '1,2,4-5'
set system option kernel cpu rcu-no-cbs '1,2,4-5'
set system option kernel disable-hpet
set system option kernel disable-mce
set system option kernel disable-mitigations
set system option kernel disable-softlockup
set system option kernel memory default-hugepage-size '2M'
set system option kernel memory disable-numa-balancing
set system option kernel memory hugepage-size 1G hugepage-count '2'
set system option kernel memory hugepage-size 2M hugepage-count '1650'
set system sflow interface 'eth1'
set system sflow server 127.0.0.1
set system sflow vpp

set vpp settings host-resources max-map-count '65535'
set vpp settings interface eth1 dpdk-options promisc
set vpp settings interface eth1 driver 'dpdk'
set vpp settings memory main-heap-page-size '2M'
set vpp settings memory main-heap-size '3G'
set vpp settings statseg size '128M'
set vpp settings unix poll-sleep-usec '222'


vyos@r14# set vpp sflow interface eth1.11
[edit]
vyos@r14# commit
[ vpp sflow ]
eth1.11 must be a VPP interface for sFlow monitoring
[[vpp sflow]] failed
Commit failed
[edit]
vyos@r14# 

raise ConfigError(
f'{interface} must be a VPP interface for sFlow monitoring'
)

# Verify sample rate is a positive integer
if 'sample_rate' in config:
try:
sample_rate = int(config['sample_rate'])
if sample_rate <= 0:
raise ConfigError('sFlow sample rate must be a positive integer')
except ValueError:
raise ConfigError('sFlow sample rate must be a valid integer')

# Verify that system sflow has enable-vpp defined
if 'system_sflow' not in config or 'vpp' not in config.get('system_sflow', {}):
raise ConfigError(
'sFlow enable-vpp must be defined under system sflow configuration'
)


def generate(config):
# No templates to render for sFlow
pass


def apply(config):
# Initialize VPP control API
vpp = VPPControl(attempts=20, interval=500)

if 'remove' in config:
# Disable sFlow on all interfaces
for interface in config.get('effective', {}).get('interface', []):
vpp.cli_cmd(f'sflow enable-disable {interface} disable')
return None

# Configure sample rate if specified
if 'sample_rate' in config:
vpp.cli_cmd(f'sflow sampling-rate {config["sample_rate"]}')

# Configure interfaces
if 'interface' in config:
# Enable sFlow on specified interfaces
for interface in config['interface']:
vpp.cli_cmd(f'sflow enable-disable {interface}')

# Disable sFlow on interfaces that were removed from config
effective_interfaces = config.get('effective', {}).get('interface', [])
if effective_interfaces:
for interface in effective_interfaces:
if interface not in config['interface']:
vpp.cli_cmd(f'sflow enable-disable {interface} disable')


if __name__ == '__main__':
try:
c = get_config()
verify(c)
generate(c)
apply(c)
except ConfigError as e:
print(e)
exit(1)