b2e78569c5
Add a new oslo_concurrency.prlimit module which is written to be used on the command line: python -m oslo_concurrency.prlimit --rss=RSS -- program arg1 ... This module calls setrlimit() to restrict the resources and then executes the program. Its command line is written to be the same than the Linux prlimit system program. Add a new ProcessLimits class processutils: resource limits on a process. Add an optional prlimit parameter to process_utils.execute(). If the parameter is used, wrap the command through the new oslo_concurrency prlimit wrapper. Linux provides a prlimit command line tool which implements the same feature (and even more), but it requires util-linux v2.21, and OpenStack targets other operating systems like Solaris and FreeBSD. Change-Id: Ib40aa62958ab9c157a2bd51d7ff3edb445556285 Related-Bug: 1449062
90 lines
2.8 KiB
Python
90 lines
2.8 KiB
Python
# Copyright 2016 Red Hat.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from __future__ import print_function
|
|
|
|
import argparse
|
|
import os
|
|
import resource
|
|
import sys
|
|
|
|
USAGE_PROGRAM = ('%s -m oslo_concurrency.prlimit'
|
|
% os.path.basename(sys.executable))
|
|
|
|
RESOURCES = (
|
|
# argparse argument => resource
|
|
('as', resource.RLIMIT_AS),
|
|
('nofile', resource.RLIMIT_NOFILE),
|
|
('rss', resource.RLIMIT_RSS),
|
|
)
|
|
|
|
|
|
def parse_args():
|
|
parser = argparse.ArgumentParser(description='prlimit', prog=USAGE_PROGRAM)
|
|
parser.add_argument('--as', type=int,
|
|
help='Address space limit in bytes')
|
|
parser.add_argument('--nofile', type=int,
|
|
help='Maximum number of open files')
|
|
parser.add_argument('--rss', type=int,
|
|
help='Maximum Resident Set Size (RSS) in bytes')
|
|
parser.add_argument('program',
|
|
help='Program (absolute path)')
|
|
parser.add_argument('program_args', metavar="arg", nargs='...',
|
|
help='Program parameters')
|
|
|
|
args = parser.parse_args()
|
|
return args
|
|
|
|
|
|
def main():
|
|
args = parse_args()
|
|
|
|
program = args.program
|
|
if not os.path.isabs(program):
|
|
# program uses a relative path: try to find the absolute path
|
|
# to the executable
|
|
if sys.version_info >= (3, 3):
|
|
import shutil
|
|
program_abs = shutil.which(program)
|
|
else:
|
|
import distutils.spawn
|
|
program_abs = distutils.spawn.find_executable(program)
|
|
if program_abs:
|
|
program = program_abs
|
|
|
|
for arg_name, rlimit in RESOURCES:
|
|
value = getattr(args, arg_name)
|
|
if value is None:
|
|
continue
|
|
try:
|
|
resource.setrlimit(rlimit, (value, value))
|
|
except ValueError as exc:
|
|
print("%s: failed to set the %s resource limit: %s"
|
|
% (USAGE_PROGRAM, arg_name.upper(), exc),
|
|
file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
try:
|
|
os.execv(program, [program] + args.program_args)
|
|
except Exception as exc:
|
|
print("%s: failed to execute %s: %s"
|
|
% (USAGE_PROGRAM, program, exc),
|
|
file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|