Add tests for the pfd: (pre-opened file descriptor) passive stream
provider.  Test cases cover:

  - Basic listen and accept via ovsdb-server.
  - JSON-RPC request/reply over pfd.
  - Invalid fd number (fd 99, not open).
  - Fd that is not a socket (regular file).
  - Fd that is a socket but not listening.
  - Runtime rejection via ovs-appctl add-remote.
  - Runtime rejection via database remote insertion (string column).
  - Runtime rejection via Manager table insertion (UUID ref column).

Each test uses a small Python helper to create or set up file
descriptors and then exec the server with --remote=pfd:<fd>.

Signed-off-by: Timothy Redaelli <[email protected]>
---
 tests/automake.mk   |   1 +
 tests/pfd-stream.at | 248 ++++++++++++++++++++++++++++++++++++++++++++
 tests/testsuite.at  |   1 +
 3 files changed, 250 insertions(+)
 create mode 100644 tests/pfd-stream.at

diff --git a/tests/automake.mk b/tests/automake.mk
index da569b022..50006e675 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -65,6 +65,7 @@ TESTSUITE_AT = \
        tests/json.at \
        tests/jsonrpc.at \
        tests/jsonrpc-py.at \
+       tests/pfd-stream.at \
        tests/pmd.at \
        tests/alb.at \
        tests/tunnel.at \
diff --git a/tests/pfd-stream.at b/tests/pfd-stream.at
new file mode 100644
index 000000000..95a8cba15
--- /dev/null
+++ b/tests/pfd-stream.at
@@ -0,0 +1,248 @@
+AT_BANNER([pfd stream - pre-opened file descriptor])
+
+AT_SETUP([pfd stream - basic listen and accept])
+AT_KEYWORDS([pfd])
+AT_SKIP_IF([test "$IS_WIN32" = "yes"])
+ordinal_schema > schema
+AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore])
+on_exit 'kill `cat *.pid`'
+
+# Use Python to create a listening socket and exec ovsdb-server with
+# that fd as --remote=pfd:<fd>.
+AT_DATA([serve.py], [[
+import os, socket, sys
+s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+s.bind(sys.argv[1])
+s.listen(64)
+fd = s.fileno()
+os.set_inheritable(fd, True)
+argv = sys.argv[2:]
+argv[argv.index('PFD')] = '--remote=pfd:' + str(fd)
+os.execvp(argv[0], argv)
+]])
+AT_CHECK([$PYTHON3 serve.py db.sock \
+          ovsdb-server --detach --no-chdir --log-file --pidfile db PFD],
+         [0], [ignore], [ignore])
+
+OVS_WAIT_UNTIL([test -e ovsdb-server.pid])
+
+# Connect via the socket and run a simple transaction.
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes], [0], [stdout])
+AT_CHECK([grep -c pfd stdout], [0], [1
+])
+
+AT_CHECK([ovsdb-client list-dbs unix:db.sock], [0], [stdout])
+AT_CHECK([grep ordinals stdout], [0], [ignore])
+
+OVSDB_SERVER_SHUTDOWN
+AT_CLEANUP
+
+AT_SETUP([pfd stream - JSON-RPC over pfd])
+AT_KEYWORDS([pfd])
+AT_SKIP_IF([test "$IS_WIN32" = "yes"])
+on_exit 'kill `cat *.pid`'
+
+# Use Python to create a listening socket and exec test-jsonrpc.
+AT_DATA([serve.py], [[
+import os, socket, sys
+s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+s.bind(sys.argv[1])
+s.listen(64)
+fd = s.fileno()
+os.set_inheritable(fd, True)
+argv = sys.argv[2:]
+argv[argv.index('PFD')] = 'pfd:' + str(fd)
+os.execvp(argv[0], argv)
+]])
+AT_CHECK([$PYTHON3 serve.py socket \
+          ovstest test-jsonrpc --detach --no-chdir --pidfile listen PFD],
+         [0], [ignore], [ignore])
+
+OVS_WAIT_UNTIL([test -e test-jsonrpc.pid])
+
+AT_CHECK(
+  [[ovstest test-jsonrpc request unix:socket echo '[{"a": "b", "x": null}]']], 
[0],
+  [[{"error":null,"id":0,"result":[{"a":"b","x":null}]}
+]])
+AT_CLEANUP
+
+AT_SETUP([pfd stream - invalid fd])
+AT_KEYWORDS([pfd])
+AT_SKIP_IF([test "$IS_WIN32" = "yes"])
+ordinal_schema > schema
+AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore])
+on_exit 'kill `cat *.pid`'
+
+# Start ovsdb-server with an invalid pfd: remote (fd 99 should not be open).
+# ovsdb-server will start but fail to open the remote.
+AT_DATA([serve.py], [[
+import os, socket, sys
+s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+s.bind(sys.argv[1])
+s.listen(64)
+fd = s.fileno()
+os.set_inheritable(fd, True)
+# Pass a different fd number that is not a valid socket.
+os.execvp(sys.argv[2], sys.argv[2:])
+]])
+AT_CHECK([$PYTHON3 serve.py db.sock ovsdb-server --detach --no-chdir \
+          --log-file --pidfile --remote=pfd:99 --remote=punix:db.sock db],
+         [0], [ignore], [ignore])
+
+OVS_WAIT_UNTIL([test -e ovsdb-server.pid])
+OVS_WAIT_UNTIL([grep "pfd:99: not a socket" ovsdb-server.log])
+
+OVSDB_SERVER_SHUTDOWN(["/pfd:99/d"])
+AT_CLEANUP
+
+AT_SETUP([pfd stream - fd is not a socket])
+AT_KEYWORDS([pfd])
+AT_SKIP_IF([test "$IS_WIN32" = "yes"])
+ordinal_schema > schema
+AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore])
+on_exit 'kill `cat *.pid`'
+
+# Use Python to open a regular file and exec ovsdb-server with that fd.
+AT_DATA([notasock.py], [[
+import os, socket, sys
+# Create a listening socket for punix: so we can still connect.
+ls = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ls.bind('db.sock')
+ls.listen(64)
+ls_fd = ls.fileno()
+os.set_inheritable(ls_fd, True)
+# Open a regular file.
+f = open('regular-file', 'w')
+fd = f.fileno()
+os.set_inheritable(fd, True)
+os.execvp(sys.argv[1], sys.argv[1:] + [
+    '--remote=pfd:' + str(fd),
+    '--remote=pfd:' + str(ls_fd),
+])
+]])
+touch regular-file
+AT_CHECK([$PYTHON3 notasock.py ovsdb-server --detach --no-chdir \
+          --log-file --pidfile db],
+         [0], [ignore], [ignore])
+
+OVS_WAIT_UNTIL([test -e ovsdb-server.pid])
+OVS_WAIT_UNTIL([grep "not a socket" ovsdb-server.log])
+
+OVSDB_SERVER_SHUTDOWN(["/not a socket/d;/listen failed/d"])
+AT_CLEANUP
+
+AT_SETUP([pfd stream - fd is not listening])
+AT_KEYWORDS([pfd])
+AT_SKIP_IF([test "$IS_WIN32" = "yes"])
+ordinal_schema > schema
+AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore])
+on_exit 'kill `cat *.pid`'
+
+# Use Python to create a bound (not listening) socket.
+AT_DATA([notlistening.py], [[
+import os, socket, sys
+s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+s.bind('connected.sock')
+fd = s.fileno()
+os.set_inheritable(fd, True)
+os.execvp(sys.argv[1], sys.argv[1:] + ['--remote=pfd:' + str(fd)])
+]])
+AT_CHECK([$PYTHON3 notlistening.py ovsdb-server --detach --no-chdir \
+          --log-file --pidfile --remote=punix:db.sock db],
+         [0], [ignore], [ignore])
+
+OVS_WAIT_UNTIL([test -e ovsdb-server.pid])
+OVS_WAIT_UNTIL([grep "not a listening socket" ovsdb-server.log])
+
+OVSDB_SERVER_SHUTDOWN(["/not a listening socket/d;/listen failed/d"])
+AT_CLEANUP
+
+AT_SETUP([pfd stream - rejected via add-remote])
+AT_KEYWORDS([pfd])
+AT_SKIP_IF([test "$IS_WIN32" = "yes"])
+ordinal_schema > schema
+AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore])
+on_exit 'kill `cat *.pid`'
+AT_CHECK([ovsdb-server --detach --no-chdir --log-file --pidfile \
+          --remote=punix:db.sock db], [0], [ignore], [ignore])
+
+# Try to add pfd: remote via ovs-appctl; should be rejected.
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/add-remote pfd:3], [2],
+  [], [pfd: remotes can only be specified on the command line
+ovs-appctl: ovsdb-server: server returned an error
+])
+
+OVSDB_SERVER_SHUTDOWN
+AT_CLEANUP
+
+AT_SETUP([pfd stream - rejected via database])
+AT_KEYWORDS([pfd])
+AT_SKIP_IF([test "$IS_WIN32" = "yes"])
+ordinal_schema > schema
+AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore])
+on_exit 'kill `cat *.pid`'
+AT_CHECK([ovsdb-server --detach --no-chdir --log-file --pidfile \
+          --remote=punix:db.sock \
+          --remote=db:ordinals,ordinals,name db], [0], [ignore], [ignore])
+
+# Insert a row with pfd:3 as the remote name.
+AT_CHECK(
+  [[ovsdb-client transact unix:db.sock \
+    '["ordinals", {"op": "insert", "table": "ordinals",
+      "row": {"name": "pfd:3", "number": 0}}]']],
+  [0], [ignore])
+
+# Give ovsdb-server time to process the change.
+OVS_WAIT_UNTIL([grep "pfd: remotes can only be specified on the command line" 
ovsdb-server.log])
+
+# Verify the pfd:3 remote was NOT added.
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes], [0], [stdout])
+AT_CHECK([grep pfd stdout], [1])
+
+OVSDB_SERVER_SHUTDOWN(["/pfd: remotes can only be specified/d"])
+AT_CLEANUP
+
+AT_SETUP([pfd stream - rejected via Manager table])
+AT_KEYWORDS([pfd])
+AT_SKIP_IF([test "$IS_WIN32" = "yes"])
+AT_DATA([schema],
+  [[{"name": "mydb",
+     "tables": {
+       "Root": {
+         "columns": {
+           "manager_options": {
+             "type": {
+               "key": {"type": "uuid", "refTable": "Manager"},
+               "min": 0,
+               "max": "unlimited"}}}},
+       "Manager": {
+         "columns": {
+           "target": {
+             "type": "string"}}}}}
+]])
+AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore])
+AT_CHECK(
+  [[ovsdb-tool transact db \
+     '["mydb",
+       {"op": "insert",
+        "table": "Root",
+        "row": {
+          "manager_options": ["set", [["named-uuid", "x"]]]}},
+       {"op": "insert",
+        "table": "Manager",
+        "uuid-name": "x",
+        "row": {"target": "pfd:3"}}]']], [0], [ignore], [ignore])
+on_exit 'kill `cat *.pid`'
+AT_CHECK([ovsdb-server --detach --no-chdir --log-file --pidfile \
+          --remote=punix:db.sock \
+          --remote=db:mydb,Root,manager_options db], [0], [ignore], [ignore])
+
+OVS_WAIT_UNTIL([grep "pfd: remotes can only be specified on the command line" 
ovsdb-server.log])
+
+# Verify the pfd:3 remote was NOT added.
+AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-remotes], [0], [stdout])
+AT_CHECK([grep pfd stdout], [1])
+
+OVSDB_SERVER_SHUTDOWN(["/pfd: remotes can only be specified/d
+/No status column present in the Manager table/d"])
+AT_CLEANUP
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 9d77a9f51..661f295f9 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -50,6 +50,7 @@ m4_include([tests/uuid.at])
 m4_include([tests/json.at])
 m4_include([tests/jsonrpc.at])
 m4_include([tests/jsonrpc-py.at])
+m4_include([tests/pfd-stream.at])
 m4_include([tests/tunnel.at])
 m4_include([tests/tunnel-push-pop.at])
 m4_include([tests/tunnel-push-pop-ipv6.at])
-- 
2.53.0

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

Reply via email to