blob: f74d60d012ff9d4a98ee5ded9ddf6ee1b47aa49d [file] [log] [blame]
#!/usr/bin/env python3
# Copyright (c) 2017, VMware, Inc. or its affiliates.
import os
import sys
from functools import reduce
class ValidationException(Exception):
def __init__(self, message):
super().__init__(message)
self.message = message
class Dummy(object):
@staticmethod
def validate_all():
exit("resource group is not supported on this platform")
class CgroupValidation(object):
@staticmethod
def detect_cgroup_mount_point():
# Get the cgroup mount place
proc_mounts_path = "/proc/self/mounts"
if os.path.exists(proc_mounts_path):
with open(proc_mounts_path) as f:
for line in f:
mount_specs = line.split()
mount_type = mount_specs[2]
if mount_type == "cgroup":
return os.path.dirname(mount_specs[1])
if mount_type == "cgroup2" and mount_specs[1].split("/")[-1] != "unified":
return mount_specs[1]
return ""
class CgroupValidationVersionOne(CgroupValidation):
def __init__(self):
self.mount_point = self.detect_cgroup_mount_point()
self.tab = {"r": os.R_OK, "w": os.W_OK, "x": os.X_OK, "f": os.F_OK}
self.impl = "cgroup"
self.error_prefix = " is not properly configured: "
self.component_dirs = self.detect_comp_dirs()
if not self.validate_comp_dirs():
self.component_dirs = self.fallback_comp_dirs()
def validate_all(self):
"""
Check the permissions of the toplevel gpdb cgroup dirs.
The checks should keep in sync with
src/backend/utils/resgroup/cgroup-ops-v1.c
"""
if not self.mount_point:
self.die("failed to detect cgroup mount point.")
if not self.component_dirs:
self.die("failed to detect cgroup component dirs.")
self.validate_permission("cpu", "gpdb/", "rwx")
self.validate_permission("cpu", "gpdb/cgroup.procs", "rw")
self.validate_permission("cpu", "gpdb/cpu.cfs_period_us", "rw")
self.validate_permission("cpu", "gpdb/cpu.cfs_quota_us", "rw")
self.validate_permission("cpu", "gpdb/cpu.shares", "rw")
self.validate_permission("cpuacct", "gpdb/", "rwx")
self.validate_permission("cpuacct", "gpdb/cgroup.procs", "rw")
self.validate_permission("cpuacct", "gpdb/cpuacct.usage", "r")
self.validate_permission("cpuacct", "gpdb/cpuacct.stat", "r")
self.validate_permission("cpuset", "gpdb/", "rwx")
self.validate_permission("cpuset", "gpdb/cgroup.procs", "rw")
self.validate_permission("cpuset", "gpdb/cpuset.cpus", "rw")
self.validate_permission("cpuset", "gpdb/cpuset.mems", "rw")
self.validate_permission("memory", "gpdb/", "rwx")
self.validate_permission("memory", "gpdb/cgroup.procs", "rw")
self.validate_permission("memory", "gpdb/memory.usage_in_bytes", "r")
self.validate_comp_hierarchy()
def die(self, msg):
raise ValidationException("cgroup is not properly configured: {}".format(msg))
def validate_permission(self, comp, path, mode):
"""
Validate permission on path.
If path is a dir it must end with '/'.
"""
if comp not in self.component_dirs:
self.die("can't find dir of cgroup component {}".format(comp))
component_dir = self.component_dirs[comp]
fullpath = os.path.join(self.mount_point, comp, component_dir, path)
pathtype = path[-1] == "/" and "directory" or "file"
mode_bits = reduce(lambda x, y: x | y, [self.tab[x] for x in mode], 0)
try:
if not os.path.exists(fullpath):
self.die("{} '{}' does not exist".format(pathtype, fullpath))
if not os.access(fullpath, mode_bits):
self.die("{} '{}' permission denied: require permission '{}'".
format(pathtype, fullpath, mode))
except IOError as e:
self.die("can't check permission on {} '{}': {}".format(pathtype, fullpath, str(e)))
def validate_comp_dirs(self):
"""
Validate existence of cgroup component dirs.
Return True if all the components' dir exist and have good permission,
otherwise return False.
"""
for comp in self.required_comps():
if comp not in self.component_dirs:
return False
component_dir = self.component_dirs[comp]
fullpath = os.path.join(self.mount_point, comp, component_dir, 'gpdb')
if not os.access(fullpath, os.R_OK | os.W_OK | os.X_OK):
return False
return True
def validate_comp_hierarchy(self):
"""
Validate the mount hierarchy of cpu and cpuset subsystem.
Raise an error if cpu and cpuset are mounted on the same hierarchy.
"""
path = "/proc/1/cgroup"
if not os.path.exists(path):
self.die("can't check component mount hierarchy: \
file '/proc/1/cgroup' doesn't exist")
for line in open(path):
line = line.strip()
compid, compnames, comppath = line.split(":")
if not compnames or '=' in compnames:
continue
complist = compnames.split(',')
if "cpu" in complist and "cpuset" in complist:
self.die("can't mount 'cpu' and 'cpuset' on the same hierarchy")
@staticmethod
def detect_comp_dirs():
component_dirs = {}
proc_path = "/proc/1/cgroup"
if not os.path.exists(proc_path):
return component_dirs
with open(proc_path) as f:
for line in f:
line = line.strip()
compid, compnames, comppath = line.split(":")
if not compnames or '=' in compnames:
continue
for compname in compnames.split(','):
component_dirs[compname] = comppath.strip(os.path.sep)
return component_dirs
@staticmethod
def required_comps():
comps = ["cpu", "cpuacct", "cpuset", "memory"]
return comps
def fallback_comp_dirs(self):
component_dirs = {}
for comp in self.required_comps():
component_dirs[comp] = ""
return component_dirs
if __name__ == '__main__':
if sys.platform.startswith('linux'):
try:
CgroupValidationVersionOne().validate_all()
except ValidationException as e:
exit(e.message)
else:
Dummy().validate_all()