From nobody Tue Apr 7 22:01:46 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AA726C433FE for ; Wed, 19 Oct 2022 18:31:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231194AbiJSSbP (ORCPT ); Wed, 19 Oct 2022 14:31:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42164 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230233AbiJSSbF (ORCPT ); Wed, 19 Oct 2022 14:31:05 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C1DCB179985 for ; Wed, 19 Oct 2022 11:31:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1666204261; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=cCN3nXaInVHD32MhWWcbdwlbpc4R68KG15B7DnjepoI=; b=Q0IjRtod3Kr/J34Rp/DkRe8LEinY2uFHYeppzieYywpmbOwyXAlXgpkSwo6Ou/Cxa1hJCa xGyA5A9swflXVzlkAgxjdoaZZHCON+wAhEB9k/yZo+YrGEVdrSFlHO42HHS2B77QyhN8Yw O6eNqqpBJX2qmK7LSmNmbQUgj+0vs2g= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-241-Wt-r0j5kN0SP8etswylORg-1; Wed, 19 Oct 2022 14:30:57 -0400 X-MC-Unique: Wt-r0j5kN0SP8etswylORg-1 Received: from smtp.corp.redhat.com (int-mx09.intmail.prod.int.rdu2.redhat.com [10.11.54.9]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 9978D185A7AD; Wed, 19 Oct 2022 18:30:56 +0000 (UTC) Received: from RHTPC1VM0NT.redhat.com (unknown [10.22.8.238]) by smtp.corp.redhat.com (Postfix) with ESMTP id 0F23C49BB63; Wed, 19 Oct 2022 18:30:56 +0000 (UTC) From: Aaron Conole To: netdev@vger.kernel.org Cc: Pravin B Shelar , Jakub Kicinski , "David S. Miller" , Paolo Abeni , Eric Dumazet , Thomas Graf , Kevin Sprague , dev@openvswitch.org, Eelco Chaudron , Ilya Maximets , Shuah Khan , linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org Subject: [PATCH net 2/2] selftests: add openvswitch selftest suite Date: Wed, 19 Oct 2022 14:30:54 -0400 Message-Id: <20221019183054.105815-3-aconole@redhat.com> In-Reply-To: <20221019183054.105815-1-aconole@redhat.com> References: <20221019183054.105815-1-aconole@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.1 on 10.11.54.9 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Previous commit resolves a WARN splat that can be difficult to reproduce, but with the ovs-dpctl.py utility, it can be trivial. Introduce a test case which creates a DP, and then downgrades the feature set. This will include a utility 'ovs-dpctl.py' that can be extended to do additional work. Signed-off-by: Aaron Conole Signed-off-by: Kevin Sprague --- MAINTAINERS | 1 + tools/testing/selftests/Makefile | 1 + .../selftests/net/openvswitch/Makefile | 13 + .../selftests/net/openvswitch/openvswitch.sh | 216 +++++++++ .../selftests/net/openvswitch/ovs-dpctl.py | 411 ++++++++++++++++++ 5 files changed, 642 insertions(+) create mode 100644 tools/testing/selftests/net/openvswitch/Makefile create mode 100755 tools/testing/selftests/net/openvswitch/openvswitch.sh create mode 100644 tools/testing/selftests/net/openvswitch/ovs-dpctl.py diff --git a/MAINTAINERS b/MAINTAINERS index abbe88e1c50b..295a6b0fbe26 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15434,6 +15434,7 @@ S: Maintained W: http://openvswitch.org F: include/uapi/linux/openvswitch.h F: net/openvswitch/ +F: tools/testing/selftests/net/openvswitch/ =20 OPERATING PERFORMANCE POINTS (OPP) M: Viresh Kumar diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Mak= efile index 0464b2c6c1e4..f07aef7c592c 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -49,6 +49,7 @@ TARGETS +=3D net TARGETS +=3D net/af_unix TARGETS +=3D net/forwarding TARGETS +=3D net/mptcp +TARGETS +=3D net/openvswitch TARGETS +=3D netfilter TARGETS +=3D nsfs TARGETS +=3D pidfd diff --git a/tools/testing/selftests/net/openvswitch/Makefile b/tools/testi= ng/selftests/net/openvswitch/Makefile new file mode 100644 index 000000000000..2f1508abc826 --- /dev/null +++ b/tools/testing/selftests/net/openvswitch/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 + +top_srcdir =3D ../../../../.. + +CFLAGS =3D -Wall -Wl,--no-as-needed -O2 -g -I$(top_srcdir)/usr/include $(= KHDR_INCLUDES) + +TEST_PROGS :=3D openvswitch.sh + +TEST_FILES :=3D ovs-dpctl.py + +EXTRA_CLEAN :=3D test_netlink_checks + +include ../../lib.mk diff --git a/tools/testing/selftests/net/openvswitch/openvswitch.sh b/tools= /testing/selftests/net/openvswitch/openvswitch.sh new file mode 100755 index 000000000000..bebc20f157dc --- /dev/null +++ b/tools/testing/selftests/net/openvswitch/openvswitch.sh @@ -0,0 +1,216 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# +# OVS kernel module self tests + +# Kselftest framework requirement - SKIP code is 4. +ksft_skip=3D4 + +PAUSE_ON_FAIL=3Dno +VERBOSE=3D0 +TRACING=3D0 + +tests=3D" + netlink_checks ovsnl: validate netlink attrs and settings" + +info() { + [ $VERBOSE =3D 0 ] || echo $* +} + +ovs_base=3D`pwd` +sbxs=3D +sbx_add () { + info "adding sandbox '$1'" + + sbxs=3D"$sbxs $1" + + NO_BIN=3D0 + + # Create sandbox. + local d=3D"$ovs_base"/$1 + if [ -e $d ]; then + info "removing $d" + rm -rf "$d" + fi + mkdir "$d" || return 1 + ovs_setenv $1 +} + +ovs_exit_sig() { + [ -e ${ovs_dir}/cleanup ] && . "$ovs_dir/cleanup" +} + +on_exit() { + echo "$1" > ${ovs_dir}/cleanup.tmp + cat ${ovs_dir}/cleanup >> ${ovs_dir}/cleanup.tmp + mv ${ovs_dir}/cleanup.tmp ${ovs_dir}/cleanup +} + +ovs_setenv() { + sandbox=3D$1 + + ovs_dir=3D$ovs_base${1:+/$1}; export ovs_dir + + test -e ${ovs_dir}/cleanup || : > ${ovs_dir}/cleanup +} + +ovs_sbx() { + if test "X$2" !=3D X; then + (ovs_setenv $1; shift; "$@" >> ${ovs_dir}/debug.log) + else + ovs_setenv $1 + fi +} + +ovs_add_dp () { + info "Adding DP/Bridge IF: sbx:$1 dp:$2 {$3, $4, $5}" + ovs_sbx "$1" python3 $ovs_base/ovs-dpctl.py add-dp "$2" "$3" "$4" "$5" ||= return 1 + on_exit "ovs_sbx $1 python3 $ovs_base/ovs-dpctl.py del-dp $2;" +} + +usage() { + echo + echo "$0 [OPTIONS] [TEST]..." + echo "If no TEST argument is given, all tests will be run." + echo + echo "Options" + echo " -t: capture traffic via tcpdump" + echo " -v: verbose" + echo " -p: pause on failure" + echo + echo "Available tests${tests}" + exit 1 +} + +# netlink_validation +# - Create a dp +# - check no warning with "old version" simulation +test_netlink_checks () { + sbx_add "test_netlink_checks" || return 1 + + info "setting up new DP" + ovs_add_dp "test_netlink_checks" nv0 || return 1 + # now try again + PRE_TEST=3D$(dmesg | grep -E "RIP: [0-9a-fA-Fx]+:ovs_dp_cmd_new\+") + ovs_add_dp "test_netlink_checks" nv0 -V 0 || return 1 + POST_TEST=3D$(dmesg | grep -E "RIP: [0-9a-fA-Fx]+:ovs_dp_cmd_new\+") + if [ "$PRE_TEST" !=3D "$POST_TEST" ]; then + info "failed - gen warning" + return 1 + fi + + return 0 +} + +run_test() { + ( + tname=3D"$1" + tdesc=3D"$2" + + if ! lsmod | grep openvswitch >/dev/null 2>&1; then + stdbuf -o0 printf "TEST: %-60s [NOMOD]\n" "${tdesc}" + return $ksft_skip + fi + + if python3 ovs-dpctl.py help 2>&1 | \ + grep "Need to install the python" >/dev/null 2>&1; then + stdbuf -o0 printf "TEST: %-60s [PYLIB]\n" "${tdesc}" + return $ksft_skip + fi + printf "TEST: %-60s [START]\n" "${tname}" + + unset IFS + + eval test_${tname} + ret=3D$? + + if [ $ret -eq 0 ]; then + printf "TEST: %-60s [ OK ]\n" "${tdesc}" + ovs_exit_sig + rm -rf "$ovs_dir" + elif [ $ret -eq 1 ]; then + printf "TEST: %-60s [FAIL]\n" "${tdesc}" + if [ "${PAUSE_ON_FAIL}" =3D "yes" ]; then + echo + echo "Pausing. Logs in $ovs_dir/. Hit enter to continue" + read a + fi + ovs_exit_sig + [ "${PAUSE_ON_FAIL}" =3D "yes" ] || rm -rf "$ovs_dir" + exit 1 + elif [ $ret -eq $ksft_skip ]; then + printf "TEST: %-60s [SKIP]\n" "${tdesc}" + elif [ $ret -eq 2 ]; then + rm -rf test_${tname} + run_test "$1" "$2" + fi + + return $ret + ) + ret=3D$? + case $ret in + 0) + [ $all_skipped =3D true ] && [ $exitcode=3D$ksft_skip ] && exitcode=3D0 + all_skipped=3Dfalse + ;; + $ksft_skip) + [ $all_skipped =3D true ] && exitcode=3D$ksft_skip + ;; + *) + all_skipped=3Dfalse + exitcode=3D1 + ;; + esac + + return $ret +} + + +exitcode=3D0 +desc=3D0 +all_skipped=3Dtrue + +while getopts :pvt o +do + case $o in + p) PAUSE_ON_FAIL=3Dyes;; + v) VERBOSE=3D1;; + t) if which tcpdump > /dev/null 2>&1; then + TRACING=3D1 + else + echo "=3D=3D=3D tcpdump not available, tracing disabled" + fi + ;; + *) usage;; + esac +done +shift $(($OPTIND-1)) + +IFS=3D"=09 +" + +for arg do + # Check first that all requested tests are available before running any + command -v > /dev/null "test_${arg}" || { echo "=3D=3D=3D Test ${arg} not= found"; usage; } +done + +name=3D"" +desc=3D"" +for t in ${tests}; do + [ "${name}" =3D "" ] && name=3D"${t}" && continue + [ "${desc}" =3D "" ] && desc=3D"${t}" + + run_this=3D1 + for arg do + [ "${arg}" !=3D "${arg#--*}" ] && continue + [ "${arg}" =3D "${name}" ] && run_this=3D1 && break + run_this=3D0 + done + if [ $run_this -eq 1 ]; then + run_test "${name}" "${desc}" + fi + name=3D"" + desc=3D"" +done + +exit ${exitcode} diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py b/tools/t= esting/selftests/net/openvswitch/ovs-dpctl.py new file mode 100644 index 000000000000..791d76b7adcd --- /dev/null +++ b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py @@ -0,0 +1,411 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 + +# Controls the openvswitch module. Part of the kselftest suite, but +# can be used for some diagnostic purpose as well. + +import logging +import multiprocessing +import socket +import struct +import sys + +try: + from libnl.attr import NLA_NESTED, NLA_STRING, NLA_U32, NLA_UNSPEC + from libnl.attr import nla_get_string, nla_get_u32 + from libnl.attr import nla_put, nla_put_string, nla_put_u32 + from libnl.attr import nla_policy + + from libnl.error import errmsg + + from libnl.genl.ctrl import genl_ctrl_resolve + from libnl.genl.genl import genl_connect, genlmsg_parse, genlmsg_put + + from libnl.handlers import nl_cb_alloc, nl_cb_set + from libnl.handlers import NL_CB_CUSTOM, NL_CB_MSG_IN, NL_CB_VALID + from libnl.handlers import NL_OK, NL_STOP + + from libnl.linux_private.netlink import NLM_F_ACK, NLM_F_DUMP + from libnl.linux_private.netlink import NLM_F_REQUEST, NLMSG_DONE + + from libnl.msg import NL_AUTO_SEQ, nlmsg_alloc, nlmsg_hdr + + from libnl.nl import NLMSG_ERROR, nl_recvmsgs_default, nl_send_auto + from libnl.socket_ import nl_socket_alloc, nl_socket_set_cb + from libnl.socket_ import nl_socket_get_local_port +except ModuleNotFoundError: + print("Need to install the python libnl3 library.") + print("Exiting without error.") + exit(0) + + +global sk +global ovs_families + +OVS_DATAPATH_FAMILY =3D "ovs_datapath" +OVS_VPORT_FAMILY =3D "ovs_vport" +OVS_FLOW_FAMILY =3D "ovs_flow" +OVS_PACKET_FAMILY =3D "ovs_packet" +OVS_METER_FAMILY =3D "ovs_meter" +OVS_CT_LIMIT_FAMILY =3D "ovs_ct_limit" + +OVS_DATAPATH_VERSION =3D 2 +OVS_HDR_LEN =3D 4 +OVS_DP_CMD_NEW =3D 1 +OVS_DP_CMD_DEL =3D 2 +OVS_DP_CMD_GET =3D 3 +OVS_DP_CMD_SET =3D 4 + +OVS_DP_F_VPORT_PIDS =3D 1 << 1 +OVS_DP_F_DISPATCH_UPCALL_PER_CPU =3D 1 << 3 + +OVS_DP_ATTR_NAME =3D 1 +OVS_DP_ATTR_UPCALL_PID =3D 2 +OVS_DP_ATTR_STATS =3D 3 +OVS_DP_ATTR_MEGAFLOW_STATS =3D 4 +OVS_DP_ATTR_USER_FEATURES =3D 5 +OVS_DP_ATTR_PAD =3D 6 +OVS_DP_ATTR_MASKS_CACHE_SIZE =3D 7 +OVS_DP_ATTR_PER_CPU_PIDS =3D 8 +OVS_DP_ATTR_MAX =3D 8 + +OVS_VPORT_CMD_NEW =3D 1 +OVS_VPORT_CMD_DEL =3D 2 +OVS_VPORT_CMD_GET =3D 3 +OVS_VPORT_CMD_SET =3D 4 + +OVS_VPORT_ATTR_PORT_NO =3D 1 +OVS_VPORT_ATTR_TYPE =3D 2 +OVS_VPORT_ATTR_NAME =3D 3 +OVS_VPORT_ATTR_OPTIONS =3D 4 +OVS_VPORT_ATTR_UPCALL_PID =3D 5 +OVS_VPORT_ATTR_STATS =3D 6 +OVS_VPORT_ATTR_PAD =3D 7 +OVS_VPORT_ATTR_IFINDEX =3D 8 +OVS_VPORT_ATTR_NETNSID =3D 9 +OVS_VPORT_ATTR_MAX =3D 9 + +OVS_VPORT_TYPE_NETDEV =3D 1 +OVS_VPORT_TYPE_INTERNAL =3D 2 +OVS_VPORT_TYPE_GRE =3D 3 +OVS_VPORT_TYPE_VXLAN =3D 4 +OVS_VPORT_TYPE_GENEVE =3D 5 +OVS_VPORT_TYPE_MAX =3D 5 + + +def nl_sk_transaction(msg, sk, cb): + nl_socket_set_cb(sk, cb) + ret =3D nl_send_auto(sk, msg) + if ret < 0: + print("send error: ", end=3D'') + print(errmsg[abs(ret)]) + ret =3D nl_recvmsgs_default(sk) + if ret < 0: + print("recv error: ", end=3D'') + print(errmsg[abs(ret)]) + return ret + + +def if_exists(ifname): + try: + socket.if_nametoindex(ifname) + return True + except OSError: + return False + + +def get_family(ovs_family_name): + """ + Retrieve a GENL family ID via the global nl socket + Returns: family ID for the requested family name + """ + global sk + if sk is None: + raise ConnectionError("sk not correctly setup") + numid =3D genl_ctrl_resolve(sk, ovs_family_name.encode('utf-8')) + return numid + + +def dpctl_netlink_init(): + """ + Initializes the global netlink socket, and ovs familly dictionary + Returns: 0 on success, any other value is error + """ + global sk, ovs_families + sk =3D nl_socket_alloc() + ret =3D genl_connect(sk) + if ret: + print(errmsg[abs(ret)]) + sk =3D None + return ret + ovs_families =3D {} + family_probe =3D [OVS_DATAPATH_FAMILY, OVS_VPORT_FAMILY, OVS_FLOW_FAMI= LY, + OVS_PACKET_FAMILY, OVS_METER_FAMILY, OVS_CT_LIMIT_FAMI= LY] + for family in family_probe: + ovs_families[family] =3D get_family(family) + if ovs_families[family] =3D=3D -1: + return -1 + return 0 + + +def parse_dp_msg(nlh, target_dict): + dp_dict =3D {} + attrs =3D dict((i, None) for i in range(OVS_DP_ATTR_MAX)) + dp_policy =3D dict((i, None) for i in range(OVS_DP_ATTR_MAX)) + dp_policy.update({ + OVS_DP_ATTR_NAME: nla_policy(type_=3DNLA_STRING, maxlen=3D15), + OVS_DP_ATTR_UPCALL_PID: nla_policy(type_=3DNLA_U32), + OVS_DP_ATTR_STATS: nla_policy(type_=3DNLA_NESTED), + OVS_DP_ATTR_MEGAFLOW_STATS: nla_policy(type_=3DNLA_NESTED), + OVS_DP_ATTR_USER_FEATURES: nla_policy(type_=3DNLA_U32), + OVS_DP_ATTR_MASKS_CACHE_SIZE: nla_policy(type_=3DNLA_U32), + OVS_DP_ATTR_PER_CPU_PIDS: nla_policy(type_=3DNLA_UNSPEC) + }) + ret =3D genlmsg_parse(nlh, 4, attrs, OVS_DP_ATTR_MAX, dp_policy) + if ret: + print("Error parsing datapath") + return -1 + if attrs[1] is None: + print("Error?") + dp_name =3D nla_get_string(attrs[1]).decode('utf-8') + b =3D bytes(attrs[OVS_DP_ATTR_STATS].payload) + stats =3D struct.unpack("=3DQQQQ", b[:32]) + dp_dict[OVS_DP_ATTR_STATS] =3D stats + b =3D bytes(attrs[OVS_DP_ATTR_MEGAFLOW_STATS].payload) + stats =3D struct.unpack("=3DQIIQQ", b[:32]) + dp_dict[OVS_DP_ATTR_MEGAFLOW_STATS] =3D [stats[i] for i in (0, 1, 3)] + dp_dict[OVS_DP_ATTR_MASKS_CACHE_SIZE] =3D nla_get_u32( + attrs[OVS_DP_ATTR_MASKS_CACHE_SIZE]) + target_dict[dp_name] =3D dp_dict + + +def show_dp_cb(msg, dp_dict): + nlh =3D nlmsg_hdr(msg) + if nlh.nlmsg_type =3D=3D NLMSG_DONE: + retn =3D NL_STOP + parse_dp_msg(nlh, dp_dict) + retn =3D NL_OK + return retn + + +def show_vport_cb(msg, dp_vport_dict): + dp, vport_dict =3D dp_vport_dict + nlh =3D nlmsg_hdr(msg) + retn =3D None + if nlh.nlmsg_type =3D=3D NLMSG_DONE: + retn =3D NL_STOP + attrs =3D dict((i, None) for i in range(OVS_DP_ATTR_MAX)) + port_policy =3D dict((i, None) for i in range(OVS_VPORT_ATTR_MAX)) + port_policy.update({ + OVS_VPORT_ATTR_PORT_NO: nla_policy(type_=3DNLA_U32), + OVS_VPORT_ATTR_TYPE: nla_policy(type_=3DNLA_U32), + OVS_VPORT_ATTR_NAME: nla_policy(type_=3DNLA_STRING, maxlen=3D1= 5), + OVS_VPORT_ATTR_OPTIONS: nla_policy(type_=3DNLA_NESTED), + OVS_VPORT_ATTR_UPCALL_PID: nla_policy(type_=3DNLA_UNSPEC), + OVS_VPORT_ATTR_STATS: nla_policy(type_=3DNLA_NESTED), + OVS_VPORT_ATTR_IFINDEX: nla_policy(type_=3DNLA_U32), + }) + genlmsg_parse(nlh, OVS_HDR_LEN, attrs, OVS_DP_ATTR_MAX, port_policy) + if attrs[1] is not None: + port_info =3D "Port " + str(nla_get_u32(attrs[1])) + ": " + if attrs[3] is not None: + port_info +=3D nla_get_string(attrs[3]).decode('utf-8') + if attrs[OVS_VPORT_ATTR_TYPE] is not None: + port_type =3D nla_get_u32(attrs[OVS_VPORT_ATTR_TYPE]) + if port_type =3D=3D OVS_VPORT_TYPE_INTERNAL: + port_info +=3D " (internal)" + vport_dict[dp].append(port_info) + if retn is None: + retn =3D NL_OK + return retn + + +def dpctl_show_print(dp_info, vport_info): + for i in dp_info: + print("{}".format(i)) + indent =3D 2 * " " + fields =3D ("Hit", "Missed", "Lost", "Flows") + f_zip =3D zip(fields, dp_info[i][OVS_DP_ATTR_STATS]) + format_list =3D [val for pair in f_zip for val in pair] + out_string =3D indent + "Lookups: {}: {} {}: {} {}: {}\n" + out_string +=3D indent + "{}: {}" + print(out_string.format(*format_list)) + fields =3D ("Hit", "Total", "Hit") + f_zip =3D zip(fields, dp_info[i][OVS_DP_ATTR_MEGAFLOW_STATS]) + format_list =3D [val for pair in f_zip for val in pair] + out_string =3D indent + "Masks: {}: {} {}: {}\n" + out_string +=3D indent + "Cache: {}: {}" + print(out_string.format(*format_list)) + print("Caches:\n" + indent + "Masks-cache: size: {}". + format(dp_info[i][OVS_DP_ATTR_MASKS_CACHE_SIZE])) + indent =3D 4 * " " + for port in vport_info[i]: + print(indent + port) + + +def dpctl_show(dp=3DNone): + global sk, ovs_families + cb_dp_show =3D nl_cb_alloc(NL_CB_CUSTOM) + dp_info =3D {} + vport_info =3D {} + nl_cb_set(cb_dp_show, NL_CB_VALID, NL_CB_CUSTOM, show_dp_cb, dp_info) + msg_dpctl_get =3D nlmsg_alloc() + if dp is not None: + if not if_exists(dp): + print("That interface does not exist.") + return -1 + flag =3D NLM_F_REQUEST + else: + flag =3D NLM_F_DUMP + genlmsg_put(msg_dpctl_get, 0, NL_AUTO_SEQ, + ovs_families[OVS_DATAPATH_FAMILY], OVS_HDR_LEN, + flag, OVS_DP_CMD_GET, OVS_DATAPATH_VERSION) + if dp is not None: + nla_put_string(msg_dpctl_get, OVS_DP_ATTR_NAME, dp.encode('utf-8')) + nl_sk_transaction(msg_dpctl_get, sk, cb_dp_show) + vport_info =3D dict((i, []) for i in dp_info) + # for each datapath, call down and ask it to tell us its vports. + for dp in vport_info: + msg_vport_get =3D nlmsg_alloc() + ba =3D genlmsg_put(msg_vport_get, 0, NL_AUTO_SEQ, + ovs_families[OVS_VPORT_FAMILY], OVS_HDR_LEN, + NLM_F_DUMP, OVS_VPORT_CMD_GET, OVS_DATAPATH_VERSI= ON) + ba[0:OVS_HDR_LEN] =3D struct.pack('=3DI', socket.if_nametoindex(dp= )) + cb_vport_show =3D nl_cb_alloc(NL_CB_CUSTOM) + nl_cb_set(cb_vport_show, NL_CB_VALID, NL_CB_CUSTOM, + show_vport_cb, (dp, vport_info)) + nl_sk_transaction(msg_vport_get, sk, cb_vport_show) + dpctl_show_print(dp_info, vport_info) + + +def mod_cb(msg, add): + nlh =3D nlmsg_hdr(msg) + if nlh.nlmsg_type =3D=3D NLMSG_ERROR: + b =3D nlh.payload + s =3D struct.unpack('=3Di', b[:4])[0] + if s: + print(errmsg[abs(s)]) + return NL_STOP + action =3D "added" if add else "deleted" + print("Successfully {} the datapath.".format(action)) + return NL_OK + + +def dpctl_mod_dp(args, add=3DTrue, setpid=3DFalse, hdrval=3DNone): + global ovs_families, sk + + dp =3D args[0] + cmd =3D OVS_DP_CMD_NEW if add else OVS_DP_CMD_DEL + msg_dpctl_cmd =3D nlmsg_alloc() + + userfeatures =3D 0 + if hdrval is None: + hdrver =3D OVS_DATAPATH_VERSION + userfeatures =3D OVS_DP_F_VPORT_PIDS + else: + segment =3D hdrval.find(":") + if segment =3D=3D -1: + segment =3D len(hdrval) + hdrver =3D int(hdrval[:segment], 0) + if len(hdrval[:segment]): + userfeatures =3D int(hdrval[:segment], 0) + + genlmsg_put(msg_dpctl_cmd, 0, NL_AUTO_SEQ, + ovs_families[OVS_DATAPATH_FAMILY], OVS_HDR_LEN, + NLM_F_ACK, cmd, hdrver) + + nla_put_u32(msg_dpctl_cmd, OVS_DP_ATTR_UPCALL_PID, 0) + nla_put_string(msg_dpctl_cmd, OVS_DP_ATTR_NAME, dp.encode('utf-8')) + + if setpid: + userfeatures &=3D ~OVS_DP_F_VPORT_PIDS + userfeatures |=3D OVS_DP_F_DISPATCH_UPCALL_PER_CPU + procarray =3D None + nproc =3D multiprocessing.cpu_count() + for i in range(nproc): + if procarray is not None: + procarray +=3D struct.pack("=3DI", nl_socket_get_local_por= t(sk)) + else: + procarray =3D struct.pack('=3DI', nl_socket_get_local_port= (sk)) + nla_put(msg_dpctl_cmd, OVS_DP_ATTR_UPCALL_PID, len(procarray), + procarray) + nla_put_u32(msg_dpctl_cmd, OVS_DP_ATTR_USER_FEATURES, userfeatures) + cb_dp_mod =3D nl_cb_alloc(NL_CB_CUSTOM) + nl_cb_set(cb_dp_mod, NL_CB_MSG_IN, NL_CB_CUSTOM, mod_cb, add) + return nl_sk_transaction(msg_dpctl_cmd, sk, cb_dp_mod) + + +def dpctl_add_dp(dp): + setpid =3D False + dphdr =3D None + if len(dp) > 1: + for i in range(len(dp)): + if dp[i] =3D=3D '-u': + setpid =3D True + elif dp[i] =3D=3D '-V': + i +=3D 1 + dphdr =3D dp[i] + + return dpctl_mod_dp(dp, True, setpid, dphdr) + + +def dpctl_del_dp(dp): + args =3D [dp] + return dpctl_mod_dp(args, False) + + +def help(errStr=3DNone): + """ + Display a help message, include errStr if there was an error. + Return: None + """ + if errStr is None: + print("ovs-dpctl.py: openvswitch module controller") + else: + print(errStr) + print("usage:") + print(" show [DP]\t\t\tDispay information about all datapaths, or DP") + print(" add-dp DP\t\t\tAdd new datapath DP") + print(" del-dp DP\t\t\tDelete local datapath DP") + + +def main(argv): + if len(argv) < 2: + help() + return 0 + count =3D 1 + for arg in argv[1:]: + count +=3D 1 + if arg in ("-v", "--verbose"): + logging.basicConfig(level=3Dlogging.DEBUG) + if arg in ("-h", "--help", "help"): + help() + return 0 + if arg =3D=3D "show": + dpctl_netlink_init() + if len(argv) <=3D count: + dpctl_show() + else: + dpctl_show(argv[count]) + return 0 + elif arg =3D=3D "add-dp": + dpctl_netlink_init() + if len(argv) < 3: # 3rd arg should be DP name or additional = opts + help("Missing a DP name") + return -1 + else: + dpctl_add_dp(argv[count:]) + return 0 + elif arg =3D=3D "del-dp": + dpctl_netlink_init() + if len(argv) < 3: # 3rd arg MUST be DP name + help("Missing a DP name") + return -1 + else: + dpctl_del_dp(argv[count]) + return 0 + return 0 + + +if __name__ =3D=3D "__main__": + sys.exit(main(sys.argv)) --=20 2.34.3