From: Rishabh Dave Date: Wed, 29 Apr 2020 18:10:16 +0000 (+0530) Subject: qa/cephfs: run nsenter commands with superuser privileges X-Git-Tag: wip-pdonnell-testing-20200918.022351~1115^2~1 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=04d501d6ff7b84378a6074ad6c6f2f0c1b91baa3;p=ceph-ci.git qa/cephfs: run nsenter commands with superuser privileges And add a method that sets self.fuse_daemon.subproc.pid to the PID of the process that doesn't have sudo in its arguments. For example, when "sudo ceph-fuse /mnt/cephfs" is run on the shell, it launches process with arguments "ceph-fuse /mnt/cephfs". The added method gets PID of latter/child process and sets that as the fuse daemon's PID. Not doing so kills the former/parent process but the not the child process. Also, since we are around cleanup this method a bit. Fixes: https://tracker.ceph.com/issues/45339 Signed-off-by: Rishabh Dave --- diff --git a/qa/tasks/vstart_runner.py b/qa/tasks/vstart_runner.py index d957ef0862b..eb840a24af8 100644 --- a/qa/tasks/vstart_runner.py +++ b/qa/tasks/vstart_runner.py @@ -53,6 +53,7 @@ from teuthology.orchestra.run import Raw, quote from teuthology.orchestra.daemon import DaemonGroup from teuthology.orchestra.remote import Remote from teuthology.config import config as teuth_config +from teuthology.contextutil import safe_while import six import logging @@ -164,6 +165,11 @@ class LocalRemoteProcess(object): self.subproc = subproc self.stdout = stdout or BytesIO() self.stderr = stderr or BytesIO() + # this variable is meant for instance of this class named fuse_daemon. + # child process of the command launched with sudo must be killed, + # since killing parent process alone has no impact on the child + # process. + self.fuse_pid = -1 self.check_status = check_status self.exitstatus = self.returncode = None @@ -222,7 +228,10 @@ class LocalRemoteProcess(object): if self.subproc.pid and not self.finished: log.info("kill: killing pid {0} ({1})".format( self.subproc.pid, self.args)) - safe_kill(self.subproc.pid) + if self.fuse_pid != -1: + safe_kill(self.fuse_pid) + else: + safe_kill(self.subproc.pid) else: log.info("kill: already terminated ({0})".format(self.args)) @@ -585,8 +594,7 @@ class LocalKernelMount(KernelMount): if asok_conf: d = asok_conf.groups(1)[0] break - path = "{0}/client.{1}.{2}.asok".format(d, self.client_id, self.fuse_daemon.subproc.pid) - log.info("I think my launching pid was {0}".format(self.fuse_daemon.subproc.pid)) + path = "{0}/client.{1}.*.asok".format(d, self.client_id) return path def mount(self, mount_path=None, mount_fs_name=None, mount_options=[]): @@ -689,8 +697,7 @@ class LocalFuseMount(FuseMount): if asok_conf: d = asok_conf.groups(1)[0] break - path = "{0}/client.{1}.{2}.asok".format(d, self.client_id, self.fuse_daemon.subproc.pid) - log.info("I think my launching pid was {0}".format(self.fuse_daemon.subproc.pid)) + path = "{0}/client.{1}.*.asok".format(d, self.client_id) return path def mount(self, mount_path=None, mount_fs_name=None, mountpoint=None, mount_options=[]): @@ -725,29 +732,26 @@ class LocalFuseMount(FuseMount): pre_mount_conns = list_connections() log.info("Pre-mount connections: {0}".format(pre_mount_conns)) - prefix = [os.path.join(BIN_PREFIX, "ceph-fuse")] + prefix = ['sudo', 'nsenter', + '--net=/var/run/netns/{0}'.format(self.netns_name), + '--setuid', str(os.getuid()), + os.path.join(BIN_PREFIX, "ceph-fuse")] if os.getuid() != 0: prefix += ["--client_die_on_failed_dentry_invalidate=false"] - if mount_path is not None: prefix += ["--client_mountpoint={0}".format(mount_path)] - if mount_fs_name is not None: prefix += ["--client_fs={0}".format(mount_fs_name)] - prefix += mount_options; + fuse_cmd_args = prefix + ["-f", "--name", + "client.{0}".format(self.client_id), + self.mountpoint] - self.fuse_daemon = self.client_remote.run(args= - ['nsenter', - '--net=/var/run/netns/{0}'.format(self.netns_name), - ] + prefix + [ - "-f", - "--name", - "client.{0}".format(self.client_id), - self.mountpoint - ], wait=False) - - log.info("Mounting client.{0} with pid {1}".format(self.client_id, self.fuse_daemon.subproc.pid)) + self.fuse_daemon = self.client_remote.run(args=fuse_cmd_args, + wait=False) + self._set_fuse_daemon_pid() + log.info("Mounting client.{0} with pid " + "{1}".format(self.client_id, self.fuse_daemon.subproc.pid)) # Wait for the connection reference to appear in /sys waited = 0 @@ -780,6 +784,20 @@ class LocalFuseMount(FuseMount): self.gather_mount_info() self.mounted = True + def _set_fuse_daemon_pid(self): + # NOTE: When a command is launched with sudo, two processes are + # launched, one with sudo in and other without. Make sure we + # get the PID of latter one. + with safe_while(sleep=1, tries=15) as proceed: + while proceed(): + try: + sock = self.find_admin_socket() + except (RuntimeError, CommandFailedError): + continue + + self.fuse_daemon.fuse_pid = int(re.match(".*\.(\d+)\.asok$", + sock).group(1)) + break def _run_python(self, pyscript, py_version='python'): """