Skip to content

snapshot/scheduler: work without SELinux userspace and stop misreporting init errors - #4821

Draft
ThalesBarretto wants to merge 1 commit into
gluster:develfrom
ThalesBarretto:fix/snap-scheduler-selinux-status-str
Draft

ThalesBarretto wants to merge 1 commit into
gluster:develfrom
ThalesBarretto:fix/snap-scheduler-selinux-status-str

Conversation

@ThalesBarretto

Copy link
Copy Markdown
Contributor

snapshot/scheduler: work without SELinux userspace and stop misreporting init errors

snap_scheduler.py init fails on every host without SELinux userspace and says "Another snap_scheduler command is
running" while nothing else runs. Three defects in extras/snap_scheduler/snap_scheduler.py combine: the getenforce
output is compared as bytes against the str "Disabled" (never equal under Python 3, so the SELinux-disabled early
return is dead), the absence of getsebool/setsebool is not handled (FileNotFoundError from inside the operation),
and main()'s except IOError: around perform_operation() catches every OSError of the operation and labels it as a
held lock, leaking the lock fd. CentOS masks it because getsebool is present there. Details in #4820.

The change (extras/snap_scheduler/snap_scheduler.py, +55/-23)

  • get_selinux_status() / get_bool_val() / the setsebool call: spawn with universal_newlines=True so the values
    compare as str; a missing getenforce now yields "Disabled", a missing getsebool yields -1 (no boolean, which
    set_cronjob_user_share() already treats as nothing to do), a missing setsebool is logged and skipped.
    get_bool_val() also checks the grep'ed line has the three fields it indexes.
  • main(): acquire the lock in its own try; only that failure means "another command is running". perform_operation()
    runs in a separate try whose OSError is logged with the action name, printed as Failed: <error> and returned as
    INTERNAL_ERROR; a finally releases the lock and closes the fd on every path.

Behaviour on SELinux hosts is unchanged apart from the boolean now being compared correctly ("off" is finally
recognised, so the setsebool -P cron_system_cronjob_use_shares on path is reachable as intended).

Test

No new test: tests/basic/volume-snap-scheduler.t already covers init/enable/disable/list; on a host without
SELinux userspace it fails subtests 9-14 before this change and passes 16/16 after it. On CentOS it passes before and
after.

Relation to other changes

Independent of the errno-NameError fix in the same file (the two except OSError handlers around os.makedirs() in
main()), which touches different lines; both apply in either order.

Fixes: #4820

…ing init errors

`snap_scheduler.py init` fails on every host that has no SELinux userspace
(Debian and its derivatives ship none by default) and reports it as
"Another snap_scheduler command is running. Please try again after some
time." - a message that names a condition which does not exist. Three
defects combine to produce this:

1. get_selinux_status() returns the raw stdout of `getenforce`, which
   under Python 3 is bytes. set_cronjob_user_share() then compares it
   with the str "Disabled", so the early return for a host where SELinux
   is disabled is never taken and the code always proceeds to query the
   boolean. get_bool_val() has the same bytes-vs-str problem with the
   `getsebool` output it returns.

2. get_bool_val() and the `setsebool` call in set_cronjob_user_share()
   spawn their tools without guarding against the tool being absent.
   Where SELinux userspace is not installed Popen raises
   FileNotFoundError (an OSError) from inside initialise_scheduler().
   get_selinux_status() does guard `getenforce`, but turns its absence
   into a hard failure (-1, "Failed to get selinux status") instead of
   the obvious reading that there is no SELinux to configure.

3. main() wraps both the flock() acquisition and perform_operation() in
   one `except IOError:` handler that prints the "another command is
   running" message. Under Python 3 IOError is OSError, so every OSError
   raised by the operation itself - the missing tool above, but equally
   an unwritable /etc/cron.d or a failing shutil.move() - is swallowed
   and misreported, and the lock fd is leaked with the lock still held.

On CentOS/RHEL, where the test suite runs, policycoreutils is present, so
`getsebool -a` exists and the comparison bug is masked by the code simply
taking the longer path through the boolean. tests/basic/volume-snap-
scheduler.t therefore passes there and fails everywhere else: subtests
9-14 ("snap_scheduler.py init/enable/disable/list", "check_status_
scheduler") go red with the misreport above. strace shows the failing
syscall: execve of "getsebool" -> ENOENT, right after `getenforce`
printed "Disabled".

Fix all three:

- decode the tool output (universal_newlines=True) so the status and the
  boolean compare as str;
- treat a missing getenforce/getsebool/setsebool as "SELinux not
  present": get_selinux_status() returns "Disabled", get_bool_val()
  returns -1 (no boolean, which set_cronjob_user_share() already maps to
  "nothing to do"), a missing setsebool is logged and skipped;
- in main(), map only the flock() failure to ANOTHER_TRANSACTION_IN_
  PROGRESS; an error raised by the operation is logged with the action
  name, printed as "Failed: <error>" and returned as INTERNAL_ERROR, and
  the lock is always released and the fd closed.

get_bool_val() also checks that the grep'ed line has the three fields it
indexes, instead of raising IndexError on unexpected output.

With this change `snap_scheduler.py init` succeeds on a host without
SELinux userspace and tests/basic/volume-snap-scheduler.t passes there
(16/16); behaviour on SELinux hosts is unchanged apart from the boolean
now being compared correctly.

Fixes: gluster#4820
Signed-off-by: Thales Antunes de Oliveira Barretto <thales.barretto.git@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

snap_scheduler.py init fails on hosts without SELinux userspace and reports "Another snap_scheduler command is running"

1 participant