From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-qt1-f179.google.com (mail-qt1-f179.google.com [209.85.160.179]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A32E6364EB8 for ; Tue, 8 Sep 2026 17:19:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.179 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788887971; cv=none; b=K47WSGUmxRpHBR/baDvfaX+BcjHNNNL37OFn72wtIHeZj9cdzNHgWrUQMKJSk7xpzCPsjmXHUMn32Kh5iTll3atY+8wzKJauYMWwUsky2Esk511UwhWbuWPIsqH0Nd6pCSzR4iqkB8hkjw9uh+ocughDzSimrHxprJVj8WCx3nc= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788887971; c=relaxed/simple; bh=eduLNa1ORduO5B+Fudc5UsSFJ3HTzDwG02RzjX0VrvM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=OryK1/9ZidkL7OzNfJrhDoCJmAMXE6gL+qm9GTKLscwhrTVLseliyQCEdT+/sBDQdcmWuSYgsu0gfgjTGrYs/dmF8NaRdyw13D3nIMpW9IlD3zLHwrxnQXnf1ejMbsW5+Nqtk4H5qR4lb460VA+R01Tl5qVbqyHvi5TRYIWKv0A= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=AwJUWIfu; arc=none smtp.client-ip=209.85.160.179 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="AwJUWIfu" Received: by mail-qt1-f179.google.com with SMTP id d75a77b69052e-52de50e77ffso60192211cf.2 for ; Tue, 08 Sep 2026 10:19:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788887968; x=1789492768; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=HE7yLu5+B3sxxYWLK4S+CTO5Hdc/i1dk1vfXSMYF0IQ=; b=AwJUWIfu8hfbLWuzdctiB5facjNUb8CW9ZLtCDpqrwjzC+V3K09Ie8G/fBSF26Icy+ CkxsTH4w+6vttc7ECWqtdShs3uc3ztTSnFpJuJoCv/moQ6w3+ierjQVnHB6JVLS35qxy WlDxwbwjRciSw0eeU7JbNpm5XWCtK1KHhVRV/xryndPKNOWIEtHbhzicYlc6NSro3Sgc tc0loKAe1qWQjhCuzIGH5hq/AhDesf3xyDPC/57gmE9vNpgK526I0qwp6FMIZHPOOgEd bc+f3AgZQNr5inZOSoESdKg+03fmp8+ynyYVbbw+Z09yKoS4vl8JNeg05dl11HGNYq4E tREA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788887968; x=1789492768; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=HE7yLu5+B3sxxYWLK4S+CTO5Hdc/i1dk1vfXSMYF0IQ=; b=ZUCWw9mynNV5doXhPG/2HrJVySSZCsI0VquHWq97sF6Ih5ih/9ioIQtyGhvb/tCXLi zH72i9ePozFIm9ULpT7aOkcRiLvr7jS5mlslz6HSjyA0oirBQyjdc5jkDQZW4s2eyfFv 6eVwomUOjYUcx4QdRPfQQvpLMxZnxdzkcbE1yl6QIevj480EI2Q+kJBmDs2BEvb5eJBI L0U9WMmH0EmcvUn1SJfrcybjMJjd7nQGz1+/iNA3+VKkFeNa7scjSeiTuf2YgD1+oRyz kCZ4E1nfMp5jdsiwpQvsThdp0VeM76zKECuNyGDFMEaM0am5q7gZRp2WXWQ6NUvmYQ5X whYg== X-Gm-Message-State: AFuF++mkc1ldxx1aRR2mAmcDlNnESULQSIFN2e3bSfBjH8FQMbrcNBzo 94iCz07+DmS9bdbjjSMIe2YIgVDNcph1Ov4Jn0SasCqUpJwekXd5IUhxfxElUAlZ X-Gm-Gg: AYBFou15wwQokhoRsL4z3b0k463pxfboi8lj0gpHIatatt/HjNL72ZEsKK1BJ9Te7cC 3pa0blSKlSO/faXFAyuZfWdpn/+fouOHvWrAhsJvm/fZd7hcYx7bhrR3gGxVZixs3lLuyKinQhT O1hfUsMzXAjujx1o7AybW/dta1Khe1sX0euK1wvtRxKF9pIS56o1GVkIKMiNO5leJ5cPRXyJuHK EBW5ENik/V3D9+1aF1hvdQjeGLxWFSwajTHGdxNAXzniZQtPJGjooPqdRDUBl3sB2qcvxGm7KBM NKmJU5X3+qVpBElKJpST9XqsKVcjhFHnVvcaLajWDJGOTVnS6Fx+GhzNv8uUuDfSGPB57+8auxl 1G/I6go8T3z29wm2LLVBOkdNRdFthcPszLDrZBR9bFSq/YpYD2rIFNfX7j15qL6hzgre7CkY8zD W4E+BolvYVjjlpYVI9GNWfXk55JYJieSzEhxzTgikxFE7O62hMfV/85tc= X-Received: by 2002:a05:622a:1a81:b0:51c:d52:6049 with SMTP id d75a77b69052e-530548e0cb2mr354944991cf.28.1788887968120; Tue, 08 Sep 2026 10:19:28 -0700 (PDT) Received: from houminxi ([104.167.197.198]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-530541c94ddsm120081361cf.23.2026.09.08.10.19.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 08 Sep 2026 10:19:27 -0700 (PDT) From: Minxi Hou To: netdev@vger.kernel.org Cc: aconole@redhat.com, echaudro@redhat.com, i.maximets@ovn.org, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, horms@kernel.org, shuah@kernel.org, dev@openvswitch.org, linux-kselftest@vger.kernel.org, Minxi Hou Subject: [PATCH net-next v3 3/4] selftests: openvswitch: add missing docstrings in ovs-dpctl.py Date: Tue, 8 Sep 2026 13:18:58 -0400 Message-ID: <20260908171859.1967327-4-houminxi@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260908171859.1967327-1-houminxi@gmail.com> References: <20260908171859.1967327-1-houminxi@gmail.com> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Add one-line docstrings to the module and all classes and methods that pylint flags with C0114, C0115, and C0116 (87 instances). Each docstring describes the command or attribute group the code actually implements. Signed-off-by: Minxi Hou --- .../selftests/net/openvswitch/ovs-dpctl.py | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py index 502e6eb4c4b4a..dc88cb1b20c13 100644 --- a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py +++ b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0 +"""OVS datapath control utility for kernel selftests.""" # Controls the openvswitch module. Part of the kselftest suite, but # can be used for some diagnostic purpose as well. @@ -64,11 +65,13 @@ OVS_FLOW_CMD_SET = 4 UINT32_MAX = 0xFFFFFFFF def macstr(mac): + """Format MAC address bytes as colon-separated hex string.""" outstr = ":".join([f"{i:02X}" for i in mac]) return outstr def strcspn(str1, str2): + """Return index of first char in str1 that is in str2.""" tot = 0 for char in str1: if str2.find(char) != -1: @@ -78,6 +81,7 @@ def strcspn(str1, str2): def strspn(str1, str2): + """Return index of first char in str1 that is not in str2.""" tot = 0 for char in str1: if str2.find(char) == -1: @@ -87,6 +91,7 @@ def strspn(str1, str2): def intparse(statestr, defmask="0xffffffff"): + """Parse an integer with optional mask from a state string.""" totalparse = strspn(statestr, "0123456789abcdefABCDEFx/") # scan until "/" count = strspn(statestr, "x0123456789abcdefABCDEF") @@ -107,6 +112,7 @@ def intparse(statestr, defmask="0xffffffff"): def parse_flags(flag_str, flag_vals): + """Parse a flags string into bit and mask values.""" bitResult = 0 maskResult = 0 @@ -158,6 +164,7 @@ def parse_flags(flag_str, flag_vals): def parse_ct_state(statestr): + """Parse a conntrack state string into flag bits.""" ct_flags = { "new": 1 << 0, "est": 1 << 1, @@ -173,6 +180,7 @@ def parse_ct_state(statestr): def convert_mac(data): + """Convert a MAC address string with optional mask to a bytes pair.""" def to_bytes(mac): mac_split = mac.split(":") ret = bytearray([int(i, 16) for i in mac_split]) @@ -188,6 +196,7 @@ def convert_mac(data): return to_bytes(mac_str), to_bytes(mask_str) def convert_ipv4(data): + """Convert an IPv4 address/mask string to an integer tuple.""" ip, _, mask = data.partition('/') if not ip: @@ -200,6 +209,7 @@ def convert_ipv4(data): return int(ipaddress.IPv4Address(ip)), int(ipaddress.IPv4Address(mask)) def convert_ipv6(data): + """Convert an IPv6 address/mask string to a packed bytes tuple.""" ip, _, mask = data.partition('/') if not ip: @@ -212,6 +222,7 @@ def convert_ipv6(data): return ipaddress.IPv6Address(ip).packed, ipaddress.IPv6Address(mask).packed def convert_int(size): + """Return a parser for fixed-width integers with optional mask.""" def convert_int_sized(data): value, _, mask = data.partition('/') @@ -224,6 +235,7 @@ def convert_int(size): return convert_int_sized def parse_starts_block(block_str, scanstr, returnskipped, scanregex=False): + """Check for and optionally consume a prefix in block_str.""" if scanregex: m = re.search(scanstr, block_str) if m is None: @@ -250,6 +262,7 @@ def parse_starts_block(block_str, scanstr, returnskipped, scanregex=False): def parse_extract_field( block_str, fieldstr, scanfmt, convert, masked=False, defval=None ): + """Extract and convert one field from a flow string.""" if fieldstr and not block_str.startswith(fieldstr): return block_str, defval @@ -349,6 +362,7 @@ def parse_attrs(actstr, attr_desc): class ovs_dp_msg(genlmsg): + """Datapath generic netlink message with OVS version and dpifindex.""" # include the OVS version # We need a custom header rather than just being able to rely on # genlmsg because fields ends up not expressing everything correctly @@ -357,6 +371,7 @@ class ovs_dp_msg(genlmsg): class ovsactions(nla): + """OVS action attribute list (OVS_ACTION_ATTR_*).""" nla_flags = NLA_F_NESTED nla_map = ( @@ -399,6 +414,7 @@ class ovsactions(nla): ) class psample(nla): + """psample action attributes (OVS_PSAMPLE_ATTR_*).""" nla_flags = NLA_F_NESTED nla_map = ( @@ -408,6 +424,7 @@ class ovsactions(nla): ) def dpstr(self, more=False): + """Format the psample action as a dpctl string.""" args = f"group={int(self.get_attr('OVS_PSAMPLE_ATTR_GROUP'))}" cookie = self.get_attr("OVS_PSAMPLE_ATTR_COOKIE") @@ -417,6 +434,7 @@ class ovsactions(nla): return f"psample({args})" def parse(self, actstr): + """Parse a psample() action string.""" desc = ( ("group", "OVS_PSAMPLE_ATTR_GROUP", int), ("cookie", "OVS_PSAMPLE_ATTR_COOKIE", @@ -431,9 +449,11 @@ class ovsactions(nla): return actstr class push_vlan(nla): + """push_vlan action attributes (vlan_tpid/vlan_tci).""" fields = (("vlan_tpid", "!H"), ("vlan_tci", "!H")) class sample(nla): + """Sample action attributes (OVS_SAMPLE_ATTR_*).""" nla_flags = NLA_F_NESTED nla_map = ( @@ -443,6 +463,7 @@ class ovsactions(nla): ) def dpstr(self, more=False): + """Format the sample action as a dpctl string.""" args = [] prob = 100 * self.get_attr( @@ -456,6 +477,7 @@ class ovsactions(nla): return f"sample({','.join(args)})" def parse(self, actstr): + """Parse a sample() action string.""" def parse_nested_actions(actstr): subacts = ovsactions() parsed_len = subacts.parse(actstr) @@ -477,6 +499,7 @@ class ovsactions(nla): return actstr class ctact(nla): + """Conntrack action attributes (OVS_CT_ATTR_*).""" nla_flags = NLA_F_NESTED nla_map = ( @@ -493,6 +516,7 @@ class ovsactions(nla): ) class natattr(nla): + """NAT attributes for the conntrack action (OVS_NAT_ATTR_*).""" nla_flags = NLA_F_NESTED nla_map = ( @@ -509,6 +533,7 @@ class ovsactions(nla): ) def dpstr(self, more=False): + """Format the NAT attributes as a dpctl string.""" print_str = "nat(" if self.get_attr("OVS_NAT_ATTR_SRC"): @@ -547,6 +572,7 @@ class ovsactions(nla): return print_str def dpstr(self, more=False): + """Format the conntrack action as a dpctl string.""" print_str = "ct(" if self.get_attr("OVS_CT_ATTR_COMMIT") is not None: @@ -568,6 +594,7 @@ class ovsactions(nla): return print_str class userspace(nla): + """Userspace action attributes (OVS_USERSPACE_ATTR_*).""" nla_flags = NLA_F_NESTED nla_map = ( @@ -578,6 +605,7 @@ class ovsactions(nla): ) def dpstr(self, more=False): + """Format the userspace action as a dpctl string.""" print_str = "userspace(" if self.get_attr("OVS_USERSPACE_ATTR_PID") is not None: print_str += f"pid={int(self.get_attr('OVS_USERSPACE_ATTR_PID'))}," @@ -593,6 +621,7 @@ class ovsactions(nla): return print_str def parse(self, actstr): + """Parse a userspace() action string.""" attrs_desc = ( ("pid", "OVS_USERSPACE_ATTR_PID", int), ("userdata", "OVS_USERSPACE_ATTR_USERDATA", @@ -607,6 +636,7 @@ class ovsactions(nla): return actstr def dpstr(self, more=False): + """Format the action list as a dpctl string.""" print_str = "" for field in self["attrs"]: @@ -675,6 +705,7 @@ class ovsactions(nla): return print_str def parse(self, actstr): + """Parse a dpctl action string into attributes.""" totallen = len(actstr) while len(actstr) != 0: parsed = False @@ -971,6 +1002,7 @@ ovsactions.dec_ttl.actions = ovsactions class ovskey(nla): + """OVS flow key attributes (OVS_KEY_ATTR_*).""" nla_flags = NLA_F_NESTED nla_map = ( ("OVS_KEY_ATTR_UNSPEC", "none"), @@ -1009,6 +1041,7 @@ class ovskey(nla): ) class ovs_key_proto(nla): + """Base class for protocol-specific flow key fields.""" fields = ( ("src", "!H"), ("dst", "!H"), @@ -1041,6 +1074,7 @@ class ovskey(nla): ) def parse(self, flowstr, typeInst): + """Parse this protocol's key and mask fields from a flow string.""" if not flowstr.startswith(self.proto_str): return None, None @@ -1082,6 +1116,7 @@ class ovskey(nla): return flowstr, k, m def dpstr(self, masked=None, more=False): + """Format this protocol's key fields as a dpctl string.""" outstr = f"{self.proto_str}(" first = False for f in self.fields_map: @@ -1110,6 +1145,7 @@ class ovskey(nla): return outstr class ethaddr(ovs_key_proto): + """Ethernet address flow key (OVS_KEY_ATTR_ETHERNET).""" fields = ( ("src", "!6s"), ("dst", "!6s"), @@ -1151,6 +1187,7 @@ class ovskey(nla): ) class ovs_key_ipv4(ovs_key_proto): + """IPv4 flow key (OVS_KEY_ATTR_IPV4).""" fields = ( ("src", "!I"), ("dst", "!I"), @@ -1204,6 +1241,7 @@ class ovskey(nla): ) class ovs_key_ipv6(ovs_key_proto): + """IPv6 flow key (OVS_KEY_ATTR_IPV6).""" fields = ( ("src", "!16s"), ("dst", "!16s"), @@ -1260,6 +1298,7 @@ class ovskey(nla): ) class ovs_key_tcp(ovs_key_proto): + """TCP port flow key (OVS_KEY_ATTR_TCP).""" def __init__( self, data=None, @@ -1279,6 +1318,7 @@ class ovskey(nla): ) class ovs_key_udp(ovs_key_proto): + """UDP port flow key (OVS_KEY_ATTR_UDP).""" def __init__( self, data=None, @@ -1298,6 +1338,7 @@ class ovskey(nla): ) class ovs_key_sctp(ovs_key_proto): + """SCTP port flow key (OVS_KEY_ATTR_SCTP).""" def __init__( self, data=None, @@ -1317,6 +1358,7 @@ class ovskey(nla): ) class ovs_key_icmp(ovs_key_proto): + """ICMP flow key (OVS_KEY_ATTR_ICMP).""" fields = ( ("type", "B"), ("code", "B"), @@ -1348,6 +1390,7 @@ class ovskey(nla): ) class ovs_key_icmpv6(ovs_key_icmp): + """ICMPv6 flow key (OVS_KEY_ATTR_ICMPV6).""" def __init__( self, data=None, @@ -1367,6 +1410,7 @@ class ovskey(nla): ) class ovs_key_arp(ovs_key_proto): + """ARP flow key (OVS_KEY_ATTR_ARP).""" fields = ( ("sip", "!I"), ("tip", "!I"), @@ -1427,6 +1471,7 @@ class ovskey(nla): ) class ovs_key_nd(ovs_key_proto): + """IPv6 Neighbor Discovery flow key (OVS_KEY_ATTR_ND).""" fields = ( ("target", "!16s"), ("sll", "!6s"), @@ -1463,6 +1508,7 @@ class ovskey(nla): ) class ovs_key_ct_tuple_ipv4(ovs_key_proto): + """IPv4 conntrack tuple key (OVS_KEY_ATTR_CT_TUPLE_IPV4).""" fields = ( ("src", "!I"), ("dst", "!I"), @@ -1510,6 +1556,7 @@ class ovskey(nla): ) class ovs_key_ct_tuple_ipv6(nla): + """IPv6 conntrack tuple key (OVS_KEY_ATTR_CT_TUPLE_IPV6).""" fields = ( ("src", "!16s"), ("dst", "!16s"), @@ -1555,6 +1602,7 @@ class ovskey(nla): ) class ovs_key_tunnel(nla): + """Tunnel flow key attributes (OVS_TUNNEL_KEY_ATTR_*).""" nla_flags = NLA_F_NESTED nla_map = ( @@ -1578,6 +1626,7 @@ class ovskey(nla): ) def parse(self, flowstr, mask=None): + """Parse a tunnel() key string.""" if not flowstr.startswith("tunnel("): return None, None @@ -1670,6 +1719,7 @@ class ovskey(nla): return flowstr, k, mask def dpstr(self, mask=None, more=False): + """Format the tunnel key as a dpctl string.""" print_str = "tunnel(" flagsattrs = [] @@ -1712,6 +1762,7 @@ class ovskey(nla): return print_str class ovs_key_mpls(nla): + """MPLS flow key (OVS_KEY_ATTR_MPLS).""" fields = (("lse", ">I"),) # 802.1Q CFI (Canonical Format Indicator) bit, always set for Ethernet @@ -1901,6 +1952,7 @@ class ovskey(nla): return flowstr, inner_key, inner_mask def parse(self, flowstr, mask=None): + """Parse a flow key string into key and mask attributes.""" for field in ( ("OVS_KEY_ATTR_PRIORITY", "skb_priority", intparse), ("OVS_KEY_ATTR_SKB_MARK", "skb_mark", intparse), @@ -1997,6 +2049,7 @@ class ovskey(nla): return flowstr def dpstr(self, mask=None, more=False): + """Format the flow key as a dpctl string.""" print_str = "" for field in ( @@ -2166,11 +2219,13 @@ class encap_ovskey(ovskey): class OvsPacket(GenericNetlinkSocket): + """OVS packet command socket (miss/action/execute upcalls).""" OVS_PACKET_CMD_MISS = 1 # Flow table miss OVS_PACKET_CMD_ACTION = 2 # USERSPACE action OVS_PACKET_CMD_EXECUTE = 3 # Apply actions to packet class ovs_packet_msg(ovs_dp_msg): + """Packet command message attributes (OVS_PACKET_ATTR_*).""" nla_map = ( ("OVS_PACKET_ATTR_UNSPEC", "none"), ("OVS_PACKET_ATTR_PACKET", "array(uint8)"), @@ -2191,6 +2246,7 @@ class OvsPacket(GenericNetlinkSocket): self.bind(OVS_PACKET_FAMILY, OvsPacket.ovs_packet_msg) def upcall_handler(self, up=None): + """Listen for packet upcalls and dispatch them to the handler.""" print("listening on upcall packet handler:", self.epid) while True: try: @@ -2211,6 +2267,7 @@ class OvsPacket(GenericNetlinkSocket): class OvsDatapath(GenericNetlinkSocket): + """OVS datapath command socket (create/destroy/lookup).""" OVS_DP_F_VPORT_PIDS = 1 << 1 OVS_DP_F_DISPATCH_UPCALL_PER_CPU = 1 << 3 @@ -2232,6 +2289,7 @@ class OvsDatapath(GenericNetlinkSocket): ) class dpstats(nla): + """Datapath statistics (OVS_DP_ATTR_STATS).""" fields = ( ("hit", "=Q"), ("missed", "=Q"), @@ -2240,6 +2298,7 @@ class OvsDatapath(GenericNetlinkSocket): ) class megaflowstats(nla): + """Datapath megaflow statistics (OVS_DP_ATTR_MEGAFLOW_STATS).""" fields = ( ("mask_hit", "=Q"), ("masks", "=I"), @@ -2253,6 +2312,7 @@ class OvsDatapath(GenericNetlinkSocket): self.bind(OVS_DATAPATH_FAMILY, OvsDatapath.dp_cmd_msg) def info(self, dpname, ifindex=0): + """Look up a datapath by name.""" msg = OvsDatapath.dp_cmd_msg() msg["cmd"] = OVS_DP_CMD_GET msg["version"] = OVS_DATAPATH_VERSION @@ -2276,6 +2336,7 @@ class OvsDatapath(GenericNetlinkSocket): def create( self, dpname, shouldUpcall=False, versionStr=None, p=OvsPacket() ): + """Create a new datapath.""" msg = OvsDatapath.dp_cmd_msg() msg["cmd"] = OVS_DP_CMD_NEW if versionStr is None: @@ -2317,6 +2378,7 @@ class OvsDatapath(GenericNetlinkSocket): return reply def destroy(self, dpname): + """Destroy a datapath.""" msg = OvsDatapath.dp_cmd_msg() msg["cmd"] = OVS_DP_CMD_DEL msg["version"] = OVS_DATAPATH_VERSION @@ -2339,10 +2401,12 @@ class OvsDatapath(GenericNetlinkSocket): class OvsVport(GenericNetlinkSocket): + """OVS vport command socket (attach/detach/lookup).""" OVS_VPORT_TYPE_NETDEV = 1 OVS_VPORT_TYPE_INTERNAL = 2 class ovs_vport_msg(ovs_dp_msg): + """Vport command message attributes (OVS_VPORT_ATTR_*).""" nla_map = ( ("OVS_VPORT_ATTR_UNSPEC", "none"), ("OVS_VPORT_ATTR_PORT_NO", "uint32"), @@ -2357,6 +2421,7 @@ class OvsVport(GenericNetlinkSocket): ) class vportstats(nla): + """Vport statistics (OVS_VPORT_ATTR_STATS).""" fields = ( ("rx_packets", "=Q"), ("tx_packets", "=Q"), @@ -2370,6 +2435,7 @@ class OvsVport(GenericNetlinkSocket): @staticmethod def type_to_str(vport_type): + """Convert a vport type constant to its string name.""" if vport_type == OvsVport.OVS_VPORT_TYPE_NETDEV: return "netdev" if vport_type == OvsVport.OVS_VPORT_TYPE_INTERNAL: @@ -2379,6 +2445,7 @@ class OvsVport(GenericNetlinkSocket): @staticmethod def str_to_type(vport_type): + """Convert a vport type string to its constant.""" if vport_type in ["netdev", "gre", "vxlan", "geneve"]: return OvsVport.OVS_VPORT_TYPE_NETDEV if vport_type == "internal": @@ -2392,6 +2459,7 @@ class OvsVport(GenericNetlinkSocket): self.upcall_packet = packet def info(self, vport_name, dpifindex=0, portno=None): + """Get info about a vport.""" msg = OvsVport.ovs_vport_msg() msg["cmd"] = OVS_VPORT_CMD_GET @@ -2417,6 +2485,7 @@ class OvsVport(GenericNetlinkSocket): return reply def attach(self, dpindex, vport_ifname, ptype, dport): + """Create a vport and attach it to a datapath.""" msg = OvsVport.ovs_vport_msg() msg["cmd"] = OVS_VPORT_CMD_NEW @@ -2470,6 +2539,7 @@ class OvsVport(GenericNetlinkSocket): return reply def reset_upcall(self, dpindex, vport_ifname, p=None): + """Reset a vport's upcall pid.""" msg = OvsVport.ovs_vport_msg() msg["cmd"] = OVS_VPORT_CMD_SET @@ -2495,6 +2565,7 @@ class OvsVport(GenericNetlinkSocket): return reply def detach(self, dpindex, vport_ifname): + """Remove a vport from a datapath.""" msg = OvsVport.ovs_vport_msg() msg["cmd"] = OVS_VPORT_CMD_DEL @@ -2516,11 +2587,14 @@ class OvsVport(GenericNetlinkSocket): return reply def upcall_handler(self, handler=None): + """Delegate upcall handling to the packet socket.""" self.upcall_packet.upcall_handler(handler) class OvsFlow(GenericNetlinkSocket): + """OVS flow command socket (add/modify/delete/dump flows).""" class ovs_flow_msg(ovs_dp_msg): + """Flow command message attributes (OVS_FLOW_ATTR_*).""" nla_map = ( ("OVS_FLOW_ATTR_UNSPEC", "none"), ("OVS_FLOW_ATTR_KEY", "ovskey"), @@ -2536,12 +2610,14 @@ class OvsFlow(GenericNetlinkSocket): ) class flowstats(nla): + """Flow statistics (OVS_FLOW_ATTR_STATS).""" fields = ( ("packets", "=Q"), ("bytes", "=Q"), ) def dpstr(self, more=False): + """Format the flow message as a dpctl string.""" ufid = self.get_attr("OVS_FLOW_ATTR_UFID") ufid_str = "" if ufid is not None: @@ -2608,6 +2684,7 @@ class OvsFlow(GenericNetlinkSocket): return print_str def parse(self, flowstr, actstr, dpidx=0): + """Parse flow and action strings into a flow message.""" OVS_UFID_F_OMIT_KEY = 1 << 0 OVS_UFID_F_OMIT_MASK = 1 << 1 OVS_UFID_F_OMIT_ACTIONS = 1 << 2 @@ -2772,6 +2849,7 @@ class OvsFlow(GenericNetlinkSocket): return rep def miss(self, packetmsg): + """Handle a flow-table miss upcall.""" seq = packetmsg["header"]["sequence_number"] keystr = "(none)" key_field = packetmsg.get_attr("OVS_PACKET_ATTR_KEY") @@ -2784,13 +2862,16 @@ class OvsFlow(GenericNetlinkSocket): print(f"MISS upcall[{int(seq)}/{pktpres}]: {keystr}", flush=True) def execute(self, packetmsg): + """Handle a userspace execute upcall.""" print("userspace execute command", flush=True) def action(self, packetmsg): + """Handle a userspace action upcall.""" print("userspace action command", flush=True) class psample_sample(genlmsg): + """psample sample event message (PSAMPLE_ATTR_*).""" nla_map = ( ("PSAMPLE_ATTR_IIFINDEX", "none"), ("PSAMPLE_ATTR_OIFINDEX", "none"), @@ -2811,6 +2892,7 @@ class psample_sample(genlmsg): ) def dpstr(self): + """Format the psample event as a string.""" fields = [] data = "" for (attr, value) in self["attrs"]: @@ -2828,6 +2910,7 @@ class psample_sample(genlmsg): class psample_msg(Marshal): + """psample generic netlink message marshaller.""" PSAMPLE_CMD_SAMPLE = 0 PSAMPLE_CMD_GET_GROUP = 1 PSAMPLE_CMD_NEW_GROUP = 2 @@ -2837,11 +2920,13 @@ class psample_msg(Marshal): class PsampleEvent(EventSocket): + """Socket listening for psample multicast events.""" genl_family = "psample" mcast_groups = ["packets"] marshal_class = psample_msg def read_samples(self): + """Print psample events as they arrive.""" print("listening for psample events", flush=True) while True: try: @@ -2852,6 +2937,7 @@ class PsampleEvent(EventSocket): def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()): + """Print full datapath state: stats, vports, and flows.""" dp_name = dp_lookup_rep.get_attr("OVS_DP_ATTR_NAME") base_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_STATS") megaflow_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_MEGAFLOW_STATS") @@ -2887,6 +2973,7 @@ def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()): def main(argv): + """Entry point: parse arguments and run the ovs-dpctl command.""" nlmsg_atoms.encap_ovskey = encap_ovskey nlmsg_atoms.ovskey = ovskey nlmsg_atoms.ovsactions = ovsactions -- 2.55.0