From 39fbf988a495e07335efc9f176ceced9b001e8c2 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Mon, 29 Jun 2015 17:13:39 +0100 Subject: [PATCH] processutils: allow process limits to be set Add a 'limits' arg to execute() allowing a set of process resource limits to be defined. This are set between the fork and exec of the child process. The 'limits' arg is a dict, whose keys are the resource.RLIMIT_* constants and values are the desired limit as defined by resource.setrlimit() eg execute(.... limits={ resource.RLIMIT_AS: 10 * units.Gi, resource.RLIMIT_NOFILE: 8 * units.Ki }) --- oslo_concurrency/processutils.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/oslo_concurrency/processutils.py b/oslo_concurrency/processutils.py index 5958dbd..efc6186 100644 --- a/oslo_concurrency/processutils.py +++ b/oslo_concurrency/processutils.py @@ -21,6 +21,7 @@ import logging import multiprocessing import os import random +import resource import shlex import signal import time @@ -86,11 +87,21 @@ class NoRootWrapSpecified(Exception): super(NoRootWrapSpecified, self).__init__(message) -def _subprocess_setup(): +def _subprocess_setup(limits): # Python installs a SIGPIPE handler by default. This is usually not what # non-Python subprocesses expect. signal.signal(signal.SIGPIPE, signal.SIG_DFL) + if limits is not None: + for resource in limits: + value = limits[resource] + # Allow plain 'int' for convenience when hard + # and soft limits are the same (which is the + # common case) + if type(value) == int: + value = (value, value) + resources.setrlimit(resource, value) + LOG_ALL_ERRORS = 1 LOG_FINAL_ERROR = 2 @@ -147,6 +158,8 @@ def execute(*cmd, **kwargs): :param binary: On Python 3, return stdout and stderr as bytes if binary is True, as Unicode otherwise. :type binary: boolean + :param limits: Update process resource limits + :type limits: dict of resource.RLIMIT constants to values :returns: (stdout, stderr) from process execution :raises: :class:`UnknownArgumentError` on receiving unknown arguments @@ -167,6 +180,7 @@ def execute(*cmd, **kwargs): loglevel = kwargs.pop('loglevel', logging.DEBUG) log_errors = kwargs.pop('log_errors', None) binary = kwargs.pop('binary', False) + limits = kwargs.pop('limits', None) if isinstance(check_exit_code, bool): ignore_exit_code = not check_exit_code @@ -204,6 +218,9 @@ def execute(*cmd, **kwargs): _PIPE = subprocess.PIPE # pylint: disable=E1101 if os.name == 'nt': + if limits is not None: + raise InvalidArgumentError( + _('Process limits not supported on this platform')) preexec_fn = None close_fds = False else: @@ -215,7 +232,7 @@ def execute(*cmd, **kwargs): stdout=_PIPE, stderr=_PIPE, close_fds=close_fds, - preexec_fn=preexec_fn, + preexec_fn=lambda: preexec_fn(limits), shell=shell, cwd=cwd, env=env_variables) -- 2.4.3