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 <[email protected]>
Signed-off-by: Kevin Sprague <[email protected]>
---
 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/
 
 OPERATING PERFORMANCE POINTS (OPP)
 M:     Viresh Kumar <[email protected]>
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 0464b2c6c1e4..f07aef7c592c 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -49,6 +49,7 @@ TARGETS += net
 TARGETS += net/af_unix
 TARGETS += net/forwarding
 TARGETS += net/mptcp
+TARGETS += net/openvswitch
 TARGETS += netfilter
 TARGETS += nsfs
 TARGETS += pidfd
diff --git a/tools/testing/selftests/net/openvswitch/Makefile 
b/tools/testing/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 = ../../../../..
+
+CFLAGS =  -Wall -Wl,--no-as-needed -O2 -g -I$(top_srcdir)/usr/include 
$(KHDR_INCLUDES)
+
+TEST_PROGS := openvswitch.sh
+
+TEST_FILES := ovs-dpctl.py
+
+EXTRA_CLEAN := 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=4
+
+PAUSE_ON_FAIL=no
+VERBOSE=0
+TRACING=0
+
+tests="
+       netlink_checks                          ovsnl: validate netlink attrs 
and settings"
+
+info() {
+    [ $VERBOSE = 0 ] || echo $*
+}
+
+ovs_base=`pwd`
+sbxs=
+sbx_add () {
+       info "adding sandbox '$1'"
+
+       sbxs="$sbxs $1"
+
+       NO_BIN=0
+
+       # Create sandbox.
+       local d="$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=$1
+
+       ovs_dir=$ovs_base${1:+/$1}; export ovs_dir
+
+       test -e ${ovs_dir}/cleanup || : > ${ovs_dir}/cleanup
+}
+
+ovs_sbx() {
+       if test "X$2" != 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=$(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=$(dmesg | grep -E "RIP: [0-9a-fA-Fx]+:ovs_dp_cmd_new\+")
+       if [ "$PRE_TEST" != "$POST_TEST" ]; then
+               info "failed - gen warning"
+               return 1
+       fi
+
+       return 0
+}
+
+run_test() {
+       (
+       tname="$1"
+       tdesc="$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=$?
+
+       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}" = "yes" ]; then
+                       echo
+                       echo "Pausing. Logs in $ovs_dir/. Hit enter to continue"
+                       read a
+               fi
+               ovs_exit_sig
+               [ "${PAUSE_ON_FAIL}" = "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=$?
+       case $ret in
+               0)
+                       [ $all_skipped = true ] && [ $exitcode=$ksft_skip ] && 
exitcode=0
+                       all_skipped=false
+               ;;
+               $ksft_skip)
+                       [ $all_skipped = true ] && exitcode=$ksft_skip
+               ;;
+               *)
+                       all_skipped=false
+                       exitcode=1
+               ;;
+       esac
+
+       return $ret
+}
+
+
+exitcode=0
+desc=0
+all_skipped=true
+
+while getopts :pvt o
+do
+       case $o in
+       p) PAUSE_ON_FAIL=yes;;
+       v) VERBOSE=1;;
+       t) if which tcpdump > /dev/null 2>&1; then
+               TRACING=1
+          else
+               echo "=== tcpdump not available, tracing disabled"
+          fi
+          ;;
+       *) usage;;
+       esac
+done
+shift $(($OPTIND-1))
+
+IFS="  
+"
+
+for arg do
+       # Check first that all requested tests are available before running any
+       command -v > /dev/null "test_${arg}" || { echo "=== Test ${arg} not 
found"; usage; }
+done
+
+name=""
+desc=""
+for t in ${tests}; do
+       [ "${name}" = "" ]      && name="${t}"  && continue
+       [ "${desc}" = "" ]      && desc="${t}"
+
+       run_this=1
+       for arg do
+               [ "${arg}" != "${arg#--*}" ] && continue
+               [ "${arg}" = "${name}" ] && run_this=1 && break
+               run_this=0
+       done
+       if [ $run_this -eq 1 ]; then
+               run_test "${name}" "${desc}"
+       fi
+       name=""
+       desc=""
+done
+
+exit ${exitcode}
diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py 
b/tools/testing/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 = "ovs_datapath"
+OVS_VPORT_FAMILY = "ovs_vport"
+OVS_FLOW_FAMILY = "ovs_flow"
+OVS_PACKET_FAMILY = "ovs_packet"
+OVS_METER_FAMILY = "ovs_meter"
+OVS_CT_LIMIT_FAMILY = "ovs_ct_limit"
+
+OVS_DATAPATH_VERSION = 2
+OVS_HDR_LEN = 4
+OVS_DP_CMD_NEW = 1
+OVS_DP_CMD_DEL = 2
+OVS_DP_CMD_GET = 3
+OVS_DP_CMD_SET = 4
+
+OVS_DP_F_VPORT_PIDS = 1 << 1
+OVS_DP_F_DISPATCH_UPCALL_PER_CPU = 1 << 3
+
+OVS_DP_ATTR_NAME = 1
+OVS_DP_ATTR_UPCALL_PID = 2
+OVS_DP_ATTR_STATS = 3
+OVS_DP_ATTR_MEGAFLOW_STATS = 4
+OVS_DP_ATTR_USER_FEATURES = 5
+OVS_DP_ATTR_PAD = 6
+OVS_DP_ATTR_MASKS_CACHE_SIZE = 7
+OVS_DP_ATTR_PER_CPU_PIDS = 8
+OVS_DP_ATTR_MAX = 8
+
+OVS_VPORT_CMD_NEW = 1
+OVS_VPORT_CMD_DEL = 2
+OVS_VPORT_CMD_GET = 3
+OVS_VPORT_CMD_SET = 4
+
+OVS_VPORT_ATTR_PORT_NO = 1
+OVS_VPORT_ATTR_TYPE = 2
+OVS_VPORT_ATTR_NAME = 3
+OVS_VPORT_ATTR_OPTIONS = 4
+OVS_VPORT_ATTR_UPCALL_PID = 5
+OVS_VPORT_ATTR_STATS = 6
+OVS_VPORT_ATTR_PAD = 7
+OVS_VPORT_ATTR_IFINDEX = 8
+OVS_VPORT_ATTR_NETNSID = 9
+OVS_VPORT_ATTR_MAX = 9
+
+OVS_VPORT_TYPE_NETDEV = 1
+OVS_VPORT_TYPE_INTERNAL = 2
+OVS_VPORT_TYPE_GRE = 3
+OVS_VPORT_TYPE_VXLAN = 4
+OVS_VPORT_TYPE_GENEVE = 5
+OVS_VPORT_TYPE_MAX = 5
+
+
+def nl_sk_transaction(msg, sk, cb):
+    nl_socket_set_cb(sk, cb)
+    ret = nl_send_auto(sk, msg)
+    if ret < 0:
+        print("send error: ", end='')
+        print(errmsg[abs(ret)])
+    ret = nl_recvmsgs_default(sk)
+    if ret < 0:
+        print("recv error: ", end='')
+        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 = 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 = nl_socket_alloc()
+    ret = genl_connect(sk)
+    if ret:
+        print(errmsg[abs(ret)])
+        sk = None
+        return ret
+    ovs_families = {}
+    family_probe = [OVS_DATAPATH_FAMILY, OVS_VPORT_FAMILY, OVS_FLOW_FAMILY,
+                    OVS_PACKET_FAMILY, OVS_METER_FAMILY, OVS_CT_LIMIT_FAMILY]
+    for family in family_probe:
+        ovs_families[family] = get_family(family)
+        if ovs_families[family] == -1:
+            return -1
+    return 0
+
+
+def parse_dp_msg(nlh, target_dict):
+    dp_dict = {}
+    attrs = dict((i, None) for i in range(OVS_DP_ATTR_MAX))
+    dp_policy = dict((i, None) for i in range(OVS_DP_ATTR_MAX))
+    dp_policy.update({
+        OVS_DP_ATTR_NAME: nla_policy(type_=NLA_STRING, maxlen=15),
+        OVS_DP_ATTR_UPCALL_PID: nla_policy(type_=NLA_U32),
+        OVS_DP_ATTR_STATS: nla_policy(type_=NLA_NESTED),
+        OVS_DP_ATTR_MEGAFLOW_STATS: nla_policy(type_=NLA_NESTED),
+        OVS_DP_ATTR_USER_FEATURES: nla_policy(type_=NLA_U32),
+        OVS_DP_ATTR_MASKS_CACHE_SIZE: nla_policy(type_=NLA_U32),
+        OVS_DP_ATTR_PER_CPU_PIDS: nla_policy(type_=NLA_UNSPEC)
+    })
+    ret = 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 = nla_get_string(attrs[1]).decode('utf-8')
+    b = bytes(attrs[OVS_DP_ATTR_STATS].payload)
+    stats = struct.unpack("=QQQQ", b[:32])
+    dp_dict[OVS_DP_ATTR_STATS] = stats
+    b = bytes(attrs[OVS_DP_ATTR_MEGAFLOW_STATS].payload)
+    stats = struct.unpack("=QIIQQ", b[:32])
+    dp_dict[OVS_DP_ATTR_MEGAFLOW_STATS] = [stats[i] for i in (0, 1, 3)]
+    dp_dict[OVS_DP_ATTR_MASKS_CACHE_SIZE] = nla_get_u32(
+        attrs[OVS_DP_ATTR_MASKS_CACHE_SIZE])
+    target_dict[dp_name] = dp_dict
+
+
+def show_dp_cb(msg, dp_dict):
+    nlh = nlmsg_hdr(msg)
+    if nlh.nlmsg_type == NLMSG_DONE:
+        retn = NL_STOP
+    parse_dp_msg(nlh, dp_dict)
+    retn = NL_OK
+    return retn
+
+
+def show_vport_cb(msg, dp_vport_dict):
+    dp, vport_dict = dp_vport_dict
+    nlh = nlmsg_hdr(msg)
+    retn = None
+    if nlh.nlmsg_type == NLMSG_DONE:
+        retn = NL_STOP
+    attrs = dict((i, None) for i in range(OVS_DP_ATTR_MAX))
+    port_policy = dict((i, None) for i in range(OVS_VPORT_ATTR_MAX))
+    port_policy.update({
+            OVS_VPORT_ATTR_PORT_NO: nla_policy(type_=NLA_U32),
+            OVS_VPORT_ATTR_TYPE: nla_policy(type_=NLA_U32),
+            OVS_VPORT_ATTR_NAME: nla_policy(type_=NLA_STRING, maxlen=15),
+            OVS_VPORT_ATTR_OPTIONS: nla_policy(type_=NLA_NESTED),
+            OVS_VPORT_ATTR_UPCALL_PID: nla_policy(type_=NLA_UNSPEC),
+            OVS_VPORT_ATTR_STATS: nla_policy(type_=NLA_NESTED),
+            OVS_VPORT_ATTR_IFINDEX: nla_policy(type_=NLA_U32),
+        })
+    genlmsg_parse(nlh, OVS_HDR_LEN, attrs, OVS_DP_ATTR_MAX, port_policy)
+    if attrs[1] is not None:
+        port_info = "Port " + str(nla_get_u32(attrs[1])) + ": "
+        if attrs[3] is not None:
+            port_info += nla_get_string(attrs[3]).decode('utf-8')
+            if attrs[OVS_VPORT_ATTR_TYPE] is not None:
+                port_type = nla_get_u32(attrs[OVS_VPORT_ATTR_TYPE])
+                if port_type == OVS_VPORT_TYPE_INTERNAL:
+                    port_info += " (internal)"
+    vport_dict[dp].append(port_info)
+    if retn is None:
+        retn = NL_OK
+    return retn
+
+
+def dpctl_show_print(dp_info, vport_info):
+    for i in dp_info:
+        print("{}".format(i))
+        indent = 2 * " "
+        fields = ("Hit", "Missed", "Lost", "Flows")
+        f_zip = zip(fields, dp_info[i][OVS_DP_ATTR_STATS])
+        format_list = [val for pair in f_zip for val in pair]
+        out_string = indent + "Lookups: {}: {} {}: {} {}: {}\n"
+        out_string += indent + "{}: {}"
+        print(out_string.format(*format_list))
+        fields = ("Hit", "Total", "Hit")
+        f_zip = zip(fields, dp_info[i][OVS_DP_ATTR_MEGAFLOW_STATS])
+        format_list = [val for pair in f_zip for val in pair]
+        out_string = indent + "Masks: {}: {} {}: {}\n"
+        out_string += 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 = 4 * " "
+        for port in vport_info[i]:
+            print(indent + port)
+
+
+def dpctl_show(dp=None):
+    global sk, ovs_families
+    cb_dp_show = nl_cb_alloc(NL_CB_CUSTOM)
+    dp_info = {}
+    vport_info = {}
+    nl_cb_set(cb_dp_show, NL_CB_VALID, NL_CB_CUSTOM, show_dp_cb, dp_info)
+    msg_dpctl_get = nlmsg_alloc()
+    if dp is not None:
+        if not if_exists(dp):
+            print("That interface does not exist.")
+            return -1
+        flag = NLM_F_REQUEST
+    else:
+        flag = 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 = 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 = nlmsg_alloc()
+        ba = 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_VERSION)
+        ba[0:OVS_HDR_LEN] = struct.pack('=I', socket.if_nametoindex(dp))
+        cb_vport_show = 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 = nlmsg_hdr(msg)
+    if nlh.nlmsg_type == NLMSG_ERROR:
+        b = nlh.payload
+        s = struct.unpack('=i', b[:4])[0]
+        if s:
+            print(errmsg[abs(s)])
+            return NL_STOP
+    action = "added" if add else "deleted"
+    print("Successfully {} the datapath.".format(action))
+    return NL_OK
+
+
+def dpctl_mod_dp(args, add=True, setpid=False, hdrval=None):
+    global ovs_families, sk
+
+    dp = args[0]
+    cmd = OVS_DP_CMD_NEW if add else OVS_DP_CMD_DEL
+    msg_dpctl_cmd = nlmsg_alloc()
+
+    userfeatures = 0
+    if hdrval is None:
+        hdrver = OVS_DATAPATH_VERSION
+        userfeatures = OVS_DP_F_VPORT_PIDS
+    else:
+        segment = hdrval.find(":")
+        if segment == -1:
+            segment = len(hdrval)
+        hdrver = int(hdrval[:segment], 0)
+        if len(hdrval[:segment]):
+            userfeatures = 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 &= ~OVS_DP_F_VPORT_PIDS
+        userfeatures |= OVS_DP_F_DISPATCH_UPCALL_PER_CPU
+        procarray = None
+        nproc = multiprocessing.cpu_count()
+        for i in range(nproc):
+            if procarray is not None:
+                procarray += struct.pack("=I", nl_socket_get_local_port(sk))
+            else:
+                procarray = struct.pack('=I', 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 = 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 = False
+    dphdr = None
+    if len(dp) > 1:
+        for i in range(len(dp)):
+            if dp[i] == '-u':
+                setpid = True
+            elif dp[i] == '-V':
+                i += 1
+                dphdr = dp[i]
+
+    return dpctl_mod_dp(dp, True, setpid, dphdr)
+
+
+def dpctl_del_dp(dp):
+    args = [dp]
+    return dpctl_mod_dp(args, False)
+
+
+def help(errStr=None):
+    """
+    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 = 1
+    for arg in argv[1:]:
+        count += 1
+        if arg in ("-v", "--verbose"):
+            logging.basicConfig(level=logging.DEBUG)
+        if arg in ("-h", "--help", "help"):
+            help()
+            return 0
+        if arg == "show":
+            dpctl_netlink_init()
+            if len(argv) <= count:
+                dpctl_show()
+            else:
+                dpctl_show(argv[count])
+            return 0
+        elif arg == "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 == "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__ == "__main__":
+    sys.exit(main(sys.argv))
-- 
2.34.3

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to