public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
From: David Gibson <david@gibson.dropbear.id.au>
To: passt-dev@passt.top, Stefano Brivio <sbrivio@redhat.com>
Cc: crosa@redhat.com, jarichte@redhat.com,
	David Gibson <david@gibson.dropbear.id.au>
Subject: [PATCH 23/27] tasst: Helpers for running daemons with a pidfile
Date: Tue, 27 Jun 2023 12:54:24 +1000	[thread overview]
Message-ID: <20230627025429.2209702-24-david@gibson.dropbear.id.au> (raw)
In-Reply-To: <20230627025429.2209702-1-david@gibson.dropbear.id.au>

When running commands through nstool, stopping them directly with the
stop() method isn't reliable, because that just kills the nstool exec
process not the actual process.  At least for programs that create their
own pidfile we can do better by using that to kill off the process when
we're done, including during cleanup on failure.  This extends the
Site.bg() method with a pidfile parameter which will allow cleanup by this
means.

There's also the case of processes that start in the foreground then
daemonize themselves.  We'd like to clean those up via pidfile as well, so
add a Site.daemon() method to do exactly that.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 test/tasst/exesite.py | 31 +++++++++++++++++++++++++++----
 1 file changed, 27 insertions(+), 4 deletions(-)

diff --git a/test/tasst/exesite.py b/test/tasst/exesite.py
index 1f34eee9..6414c73f 100644
--- a/test/tasst/exesite.py
+++ b/test/tasst/exesite.py
@@ -22,7 +22,7 @@ from avocado_classless.test import (
     assert_eq, assert_eq_unordered, assert_in, assert_raises, test_output
 )
 
-from tasst.typecheck import typecheck
+from tasst.typecheck import typecheck, typecheck_default
 
 
 class SiteProcess(contextlib.AbstractContextManager):
@@ -31,18 +31,32 @@ class SiteProcess(contextlib.AbstractContextManager):
     """
 
     def __init__(self, site, cmd, subp, *,
-                 ignore_status, context_timeout):
+                 ignore_status, context_timeout, pidfile):
         self.site = typecheck(site, Site)
         self.cmd = typecheck(cmd, str)
         self.subproc = typecheck(subp, avocado.utils.process.SubProcess)
         self.ignore_status = typecheck(ignore_status, bool)
         self.context_timeout = float(context_timeout)
+        self.pidfile = typecheck_default(pidfile, str, None)
+        self.pid = None
+
+        if pidfile is not None:
+            site.require_cmds('cat', 'kill')
 
     def __enter__(self):
         self.subproc.start()
+        if self.pidfile is not None:
+            # Wait for the PID file to be written
+            pidstr = None
+            while not pidstr:
+                pidstr = self.site.output(f'cat {self.pidfile}',
+                                          ignore_status=True)
+            self.pid = int(pidstr)
         return self
 
     def __exit__(self, *exc_details):
+        if self.pid is not None and self.subproc.poll() is None:
+            self.site.fg(f'kill -TERM {self.pid}')
         result = self.subproc.run(timeout=self.context_timeout)
         if not self.ignore_status and result.exit_status != 0:
             siteinfo = f'[{self.site.name} site]'
@@ -84,11 +98,20 @@ class Site(contextlib.AbstractContextManager):
         cmd, kwargs = self.hostify(cmd, **kwargs)
         return avocado.utils.process.SubProcess(cmd, **kwargs)
 
-    def bg(self, cmd, context_timeout=1.0, ignore_status=False, **kwargs):
+    def bg(self, cmd, *,
+           context_timeout=1.0, ignore_status=False, pidfile=None, **kwargs):
         subproc = self.subprocess(cmd, **kwargs)
         return SiteProcess(self, cmd, subproc,
                            context_timeout=context_timeout,
-                           ignore_status=ignore_status)
+                           ignore_status=ignore_status, pidfile=pidfile)
+
+    @contextlib.contextmanager
+    def daemon(self, cmd, *, pidfile, **kwargs):
+        self.require_cmds('cat', 'kill')
+        self.fg(cmd, **kwargs)
+        yield
+        pid = int(self.output(f'cat {pidfile}'))
+        self.fg(f'kill -TERM {pid}')
 
     def require_cmds(self, *cmds):
         missing = [c for c in cmds
-- 
@@ -22,7 +22,7 @@ from avocado_classless.test import (
     assert_eq, assert_eq_unordered, assert_in, assert_raises, test_output
 )
 
-from tasst.typecheck import typecheck
+from tasst.typecheck import typecheck, typecheck_default
 
 
 class SiteProcess(contextlib.AbstractContextManager):
@@ -31,18 +31,32 @@ class SiteProcess(contextlib.AbstractContextManager):
     """
 
     def __init__(self, site, cmd, subp, *,
-                 ignore_status, context_timeout):
+                 ignore_status, context_timeout, pidfile):
         self.site = typecheck(site, Site)
         self.cmd = typecheck(cmd, str)
         self.subproc = typecheck(subp, avocado.utils.process.SubProcess)
         self.ignore_status = typecheck(ignore_status, bool)
         self.context_timeout = float(context_timeout)
+        self.pidfile = typecheck_default(pidfile, str, None)
+        self.pid = None
+
+        if pidfile is not None:
+            site.require_cmds('cat', 'kill')
 
     def __enter__(self):
         self.subproc.start()
+        if self.pidfile is not None:
+            # Wait for the PID file to be written
+            pidstr = None
+            while not pidstr:
+                pidstr = self.site.output(f'cat {self.pidfile}',
+                                          ignore_status=True)
+            self.pid = int(pidstr)
         return self
 
     def __exit__(self, *exc_details):
+        if self.pid is not None and self.subproc.poll() is None:
+            self.site.fg(f'kill -TERM {self.pid}')
         result = self.subproc.run(timeout=self.context_timeout)
         if not self.ignore_status and result.exit_status != 0:
             siteinfo = f'[{self.site.name} site]'
@@ -84,11 +98,20 @@ class Site(contextlib.AbstractContextManager):
         cmd, kwargs = self.hostify(cmd, **kwargs)
         return avocado.utils.process.SubProcess(cmd, **kwargs)
 
-    def bg(self, cmd, context_timeout=1.0, ignore_status=False, **kwargs):
+    def bg(self, cmd, *,
+           context_timeout=1.0, ignore_status=False, pidfile=None, **kwargs):
         subproc = self.subprocess(cmd, **kwargs)
         return SiteProcess(self, cmd, subproc,
                            context_timeout=context_timeout,
-                           ignore_status=ignore_status)
+                           ignore_status=ignore_status, pidfile=pidfile)
+
+    @contextlib.contextmanager
+    def daemon(self, cmd, *, pidfile, **kwargs):
+        self.require_cmds('cat', 'kill')
+        self.fg(cmd, **kwargs)
+        yield
+        pid = int(self.output(f'cat {pidfile}'))
+        self.fg(f'kill -TERM {pid}')
 
     def require_cmds(self, *cmds):
         missing = [c for c in cmds
-- 
2.41.0


  parent reply	other threads:[~2023-06-27  2:54 UTC|newest]

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-06-27  2:54 [PATCH 00/27] RFC: Start converting passt & pasta tests to Avocado using special plugin David Gibson
2023-06-27  2:54 ` [PATCH 01/27] avocado: Make a duplicate copy of testsuite for comparison purposes David Gibson
2023-06-27  2:54 ` [PATCH 02/27] avocado: Don't double download assets for test/ and oldtest/ David Gibson
2023-06-27  2:54 ` [PATCH 03/27] avocado: Move static checkers to avocado David Gibson
2023-06-27  2:54 ` [PATCH 04/27] avocado: Introduce "avocado-classless" plugin, runner and outline David Gibson
2023-06-27  2:54 ` [PATCH 05/27] avocado, test: Add static checkers for Python code David Gibson
2023-06-27  2:54 ` [PATCH 06/27] avocado: Resolver implementation for avocado-classless plugin David Gibson
2023-06-27  2:54 ` [PATCH 07/27] avocado: Add basic assertion helpers to " David Gibson
2023-06-27  2:54 ` [PATCH 08/27] tasst, avocado: Introduce library of common test helpers David Gibson
2023-06-27  2:54 ` [PATCH 09/27] avocado-classless: Test matrices by composition David Gibson
2023-06-27  2:54 ` [PATCH 10/27] tasst: Helper functions for executing commands in different places David Gibson
2023-06-27  2:54 ` [PATCH 11/27] avocado-classless: Allow overriding default timeout David Gibson
2023-06-27  2:54 ` [PATCH 12/27] avocado: Convert build tests to avocado David Gibson
2023-06-27  2:54 ` [PATCH 13/27] tasst: Add helpers for running background commands on sites David Gibson
2023-06-27  2:54 ` [PATCH 14/27] tasst: Add helper to get network interface names for a site David Gibson
2023-06-27  2:54 ` [PATCH 15/27] tasst: Add helpers to run commands with nstool David Gibson
2023-06-27  2:54 ` [PATCH 16/27] tasst: Add ifup and network address helpers to Site David Gibson
2023-06-27  2:54 ` [PATCH 17/27] tasst: Helper for creating veth devices between namespaces David Gibson
2023-06-27  2:54 ` [PATCH 18/27] tasst: Add helper for getting MTU of a network interface David Gibson
2023-06-27  2:54 ` [PATCH 19/27] tasst: Add helper to wait for IP address to appear David Gibson
2023-06-27  2:54 ` [PATCH 20/27] tasst: Add helpers for getting a site's routes David Gibson
2023-06-27  2:54 ` [PATCH 21/27] tasst: Helpers to test transferring data between sites David Gibson
2023-06-27  2:54 ` [PATCH 22/27] tasst: IP address allocation helpers David Gibson
2023-06-27  2:54 ` David Gibson [this message]
2023-06-27  2:54 ` [PATCH 24/27] tasst: Helpers for testing NDP behaviour David Gibson
2023-06-27  2:54 ` [PATCH 25/27] tasst: Helpers for testing DHCP & DHCPv6 behaviour David Gibson
2023-06-27  2:54 ` [PATCH 26/27] tasst: Helpers to construct a simple network environment for tests David Gibson
2023-06-27  2:54 ` [PATCH 27/27] avocado: Convert basic pasta tests David Gibson
2023-07-05  0:30   ` Stefano Brivio
2023-07-05  3:27     ` David Gibson
2023-07-07 17:42       ` Stefano Brivio
2023-07-10  7:45         ` David Gibson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230627025429.2209702-24-david@gibson.dropbear.id.au \
    --to=david@gibson.dropbear.id.au \
    --cc=crosa@redhat.com \
    --cc=jarichte@redhat.com \
    --cc=passt-dev@passt.top \
    --cc=sbrivio@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://passt.top/passt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).