Skip to content

Commit 74cc26f

Browse files
apconoledavem330
authored andcommitted
selftests: openvswitch: add interface support
Includes an associated test to generate netns and connect interfaces, with the option to include packet tracing. This will be used in the future when flow support is added for additional test cases. Signed-off-by: Aaron Conole <aconole@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent c6d6ef3 commit 74cc26f

File tree

2 files changed

+163
-10
lines changed

2 files changed

+163
-10
lines changed

tools/testing/selftests/net/openvswitch/openvswitch.sh

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,49 @@ ovs_add_dp () {
7070
on_exit "ovs_sbx $sbxname python3 $ovs_base/ovs-dpctl.py del-dp $1;"
7171
}
7272

73+
ovs_add_if () {
74+
info "Adding IF to DP: br:$2 if:$3"
75+
ovs_sbx "$1" python3 $ovs_base/ovs-dpctl.py add-if "$2" "$3" || return 1
76+
}
77+
78+
ovs_del_if () {
79+
info "Deleting IF from DP: br:$2 if:$3"
80+
ovs_sbx "$1" python3 $ovs_base/ovs-dpctl.py del-if "$2" "$3" || return 1
81+
}
82+
83+
ovs_netns_spawn_daemon() {
84+
sbx=$1
85+
shift
86+
netns=$1
87+
shift
88+
info "spawning cmd: $*"
89+
ip netns exec $netns $* >> $ovs_dir/stdout 2>> $ovs_dir/stderr &
90+
pid=$!
91+
ovs_sbx "$sbx" on_exit "kill -TERM $pid 2>/dev/null"
92+
}
93+
94+
ovs_add_netns_and_veths () {
95+
info "Adding netns attached: sbx:$1 dp:$2 {$3, $4, $5}"
96+
ovs_sbx "$1" ip netns add "$3" || return 1
97+
on_exit "ovs_sbx $1 ip netns del $3"
98+
ovs_sbx "$1" ip link add "$4" type veth peer name "$5" || return 1
99+
on_exit "ovs_sbx $1 ip link del $4 >/dev/null 2>&1"
100+
ovs_sbx "$1" ip link set "$4" up || return 1
101+
ovs_sbx "$1" ip link set "$5" netns "$3" || return 1
102+
ovs_sbx "$1" ip netns exec "$3" ip link set "$5" up || return 1
103+
104+
if [ "$6" != "" ]; then
105+
ovs_sbx "$1" ip netns exec "$3" ip addr add "$6" dev "$5" \
106+
|| return 1
107+
fi
108+
109+
ovs_add_if "$1" "$2" "$4" || return 1
110+
[ $TRACING -eq 1 ] && ovs_netns_spawn_daemon "$1" "$ns" \
111+
tcpdump -i any -s 65535
112+
113+
return 0
114+
}
115+
73116
usage() {
74117
echo
75118
echo "$0 [OPTIONS] [TEST]..."
@@ -101,6 +144,18 @@ test_netlink_checks () {
101144
return 1
102145
fi
103146

147+
ovs_add_netns_and_veths "test_netlink_checks" nv0 left left0 l0 || \
148+
return 1
149+
ovs_add_netns_and_veths "test_netlink_checks" nv0 right right0 r0 || \
150+
return 1
151+
[ $(python3 $ovs_base/ovs-dpctl.py show nv0 | grep port | \
152+
wc -l) == 3 ] || \
153+
return 1
154+
ovs_del_if "test_netlink_checks" nv0 right0 || return 1
155+
[ $(python3 $ovs_base/ovs-dpctl.py show nv0 | grep port | \
156+
wc -l) == 2 ] || \
157+
return 1
158+
104159
return 0
105160
}
106161

tools/testing/selftests/net/openvswitch/ovs-dpctl.py

Lines changed: 108 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ class ovs_dp_msg(genlmsg):
5050

5151

5252
class OvsDatapath(GenericNetlinkSocket):
53-
5453
OVS_DP_F_VPORT_PIDS = 1 << 1
5554
OVS_DP_F_DISPATCH_UPCALL_PER_CPU = 1 << 3
5655

@@ -170,6 +169,12 @@ def destroy(self, dpname):
170169

171170

172171
class OvsVport(GenericNetlinkSocket):
172+
OVS_VPORT_TYPE_NETDEV = 1
173+
OVS_VPORT_TYPE_INTERNAL = 2
174+
OVS_VPORT_TYPE_GRE = 3
175+
OVS_VPORT_TYPE_VXLAN = 4
176+
OVS_VPORT_TYPE_GENEVE = 5
177+
173178
class ovs_vport_msg(ovs_dp_msg):
174179
nla_map = (
175180
("OVS_VPORT_ATTR_UNSPEC", "none"),
@@ -197,17 +202,30 @@ class vportstats(nla):
197202
)
198203

199204
def type_to_str(vport_type):
200-
if vport_type == 1:
205+
if vport_type == OvsVport.OVS_VPORT_TYPE_NETDEV:
201206
return "netdev"
202-
elif vport_type == 2:
207+
elif vport_type == OvsVport.OVS_VPORT_TYPE_INTERNAL:
203208
return "internal"
204-
elif vport_type == 3:
209+
elif vport_type == OvsVport.OVS_VPORT_TYPE_GRE:
205210
return "gre"
206-
elif vport_type == 4:
211+
elif vport_type == OvsVport.OVS_VPORT_TYPE_VXLAN:
207212
return "vxlan"
208-
elif vport_type == 5:
213+
elif vport_type == OvsVport.OVS_VPORT_TYPE_GENEVE:
209214
return "geneve"
210-
return "unknown:%d" % vport_type
215+
raise ValueError("Unknown vport type:%d" % vport_type)
216+
217+
def str_to_type(vport_type):
218+
if vport_type == "netdev":
219+
return OvsVport.OVS_VPORT_TYPE_NETDEV
220+
elif vport_type == "internal":
221+
return OvsVport.OVS_VPORT_TYPE_INTERNAL
222+
elif vport_type == "gre":
223+
return OvsVport.OVS_VPORT_TYPE_INTERNAL
224+
elif vport_type == "vxlan":
225+
return OvsVport.OVS_VPORT_TYPE_VXLAN
226+
elif vport_type == "geneve":
227+
return OvsVport.OVS_VPORT_TYPE_GENEVE
228+
raise ValueError("Unknown vport type: '%s'" % vport_type)
211229

212230
def __init__(self):
213231
GenericNetlinkSocket.__init__(self)
@@ -238,8 +256,51 @@ def info(self, vport_name, dpifindex=0, portno=None):
238256
raise ne
239257
return reply
240258

259+
def attach(self, dpindex, vport_ifname, ptype):
260+
msg = OvsVport.ovs_vport_msg()
261+
262+
msg["cmd"] = OVS_VPORT_CMD_NEW
263+
msg["version"] = OVS_DATAPATH_VERSION
264+
msg["reserved"] = 0
265+
msg["dpifindex"] = dpindex
266+
port_type = OvsVport.str_to_type(ptype)
267+
268+
msg["attrs"].append(["OVS_VPORT_ATTR_TYPE", port_type])
269+
msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname])
270+
msg["attrs"].append(["OVS_VPORT_ATTR_UPCALL_PID", [self.pid]])
271+
272+
try:
273+
reply = self.nlm_request(
274+
msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK
275+
)
276+
reply = reply[0]
277+
except NetlinkError as ne:
278+
raise ne
279+
return reply
280+
281+
def detach(self, dpindex, vport_ifname):
282+
msg = OvsVport.ovs_vport_msg()
283+
284+
msg["cmd"] = OVS_VPORT_CMD_DEL
285+
msg["version"] = OVS_DATAPATH_VERSION
286+
msg["reserved"] = 0
287+
msg["dpifindex"] = dpindex
288+
msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname])
241289

242-
def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB()):
290+
try:
291+
reply = self.nlm_request(
292+
msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK
293+
)
294+
reply = reply[0]
295+
except NetlinkError as ne:
296+
if ne.code == errno.ENODEV:
297+
reply = None
298+
else:
299+
raise ne
300+
return reply
301+
302+
303+
def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()):
243304
dp_name = dp_lookup_rep.get_attr("OVS_DP_ATTR_NAME")
244305
base_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_STATS")
245306
megaflow_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_MEGAFLOW_STATS")
@@ -265,7 +326,6 @@ def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB()):
265326
print(" features: 0x%X" % user_features)
266327

267328
# port print out
268-
vpl = OvsVport()
269329
for iface in ndb.interfaces:
270330
rep = vpl.info(iface.ifname, ifindex)
271331
if rep is not None:
@@ -312,9 +372,25 @@ def main(argv):
312372
deldpcmd = subparsers.add_parser("del-dp")
313373
deldpcmd.add_argument("deldp", help="Datapath Name")
314374

375+
addifcmd = subparsers.add_parser("add-if")
376+
addifcmd.add_argument("dpname", help="Datapath Name")
377+
addifcmd.add_argument("addif", help="Interface name for adding")
378+
addifcmd.add_argument(
379+
"-t",
380+
"--ptype",
381+
type=str,
382+
default="netdev",
383+
choices=["netdev", "internal"],
384+
help="Interface type (default netdev)",
385+
)
386+
delifcmd = subparsers.add_parser("del-if")
387+
delifcmd.add_argument("dpname", help="Datapath Name")
388+
delifcmd.add_argument("delif", help="Interface name for adding")
389+
315390
args = parser.parse_args()
316391

317392
ovsdp = OvsDatapath()
393+
ovsvp = OvsVport()
318394
ndb = NDB()
319395

320396
if hasattr(args, "showdp"):
@@ -328,7 +404,7 @@ def main(argv):
328404

329405
if rep is not None:
330406
found = True
331-
print_ovsdp_full(rep, iface.index, ndb)
407+
print_ovsdp_full(rep, iface.index, ndb, ovsvp)
332408

333409
if not found:
334410
msg = "No DP found"
@@ -343,6 +419,28 @@ def main(argv):
343419
print("DP '%s' added" % args.adddp)
344420
elif hasattr(args, "deldp"):
345421
ovsdp.destroy(args.deldp)
422+
elif hasattr(args, "addif"):
423+
rep = ovsdp.info(args.dpname, 0)
424+
if rep is None:
425+
print("DP '%s' not found." % args.dpname)
426+
return 1
427+
rep = ovsvp.attach(rep["dpifindex"], args.addif, args.ptype)
428+
msg = "vport '%s'" % args.addif
429+
if rep and rep["header"]["error"] is None:
430+
msg += " added."
431+
else:
432+
msg += " failed to add."
433+
elif hasattr(args, "delif"):
434+
rep = ovsdp.info(args.dpname, 0)
435+
if rep is None:
436+
print("DP '%s' not found." % args.dpname)
437+
return 1
438+
rep = ovsvp.detach(rep["dpifindex"], args.delif)
439+
msg = "vport '%s'" % args.delif
440+
if rep and rep["header"]["error"] is None:
441+
msg += " removed."
442+
else:
443+
msg += " failed to remove."
346444

347445
return 0
348446

0 commit comments

Comments
 (0)