Add python bindings via the --enable-python configure flag.
Signed-off-by: Tom Hromatka <[email protected]>
---
.github/actions/setup-libcgroup/action.yml | 6 +-
.github/workflows/continuous-integration.yml | 9 +
configure.ac | 16 ++
src/Makefile.am | 3 +
src/python/.gitignore | 6 +
src/python/Makefile.am | 56 ++++
src/python/cgroup.pxd | 64 +++++
src/python/libcgroup.pyx | 274 +++++++++++++++++++
src/python/setup.py | 47 ++++
9 files changed, 479 insertions(+), 2 deletions(-)
create mode 100644 src/python/.gitignore
create mode 100644 src/python/Makefile.am
create mode 100644 src/python/cgroup.pxd
create mode 100644 src/python/libcgroup.pyx
create mode 100755 src/python/setup.py
diff --git a/.github/actions/setup-libcgroup/action.yml
b/.github/actions/setup-libcgroup/action.yml
index fae416c8a410..01628b0a3886 100644
--- a/.github/actions/setup-libcgroup/action.yml
+++ b/.github/actions/setup-libcgroup/action.yml
@@ -26,13 +26,15 @@ runs:
steps:
- run: sudo apt-get update
shell: bash
- - run: sudo apt-get install libpam-dev lcov
+ - run: sudo apt-get install libpam-dev lcov python3-pip
+ shell: bash
+ - run: sudo pip install cython
shell: bash
- run: rm -fr tests/
shell: bash
- run: ./bootstrap.sh
shell: bash
- - run: CFLAGS="$CFLAGS -g -O0 -Werror" ./configure --sysconfdir=/etc
--localstatedir=/var --enable-code-coverage
--enable-opaque-hierarchy="name=systemd"
+ - run: CFLAGS="$CFLAGS -g -O0 -Werror" ./configure --sysconfdir=/etc
--localstatedir=/var --enable-code-coverage
--enable-opaque-hierarchy="name=systemd" --enable-python
shell: bash
- run: make
shell: bash
diff --git a/.github/workflows/continuous-integration.yml
b/.github/workflows/continuous-integration.yml
index 14fc04802f86..2ded6e9d3b2a 100644
--- a/.github/workflows/continuous-integration.yml
+++ b/.github/workflows/continuous-integration.yml
@@ -75,6 +75,9 @@ jobs:
run: |
# The cgroup v1 runner hosted by Github Actions doesn't allow
# for exclusive cpusets. Thus, skip the cpuset automated test
+ pushd src/python/build/lib.*
+ export PYTHONPATH=$PYTHONPATH:$(pwd)
+ popd
./tests/ftests/ftests.py -l 10 -L ftests.log
./tests/ftests/ftests.py -l 10 -L ftests-nocontainer.log --skip 36
--no-container
- name: Archive test logs
@@ -125,6 +128,9 @@ jobs:
uses: ./.github/actions/setup-libcgroup
- name: Run functional tests
run: |
+ pushd src/python/build/lib.*
+ export PYTHONPATH=$PYTHONPATH:$(pwd)
+ popd
pushd tests/ftests
make check
popd
@@ -169,6 +175,9 @@ jobs:
uses: ./.github/actions/setup-libcgroup
- name: Run functional tests
run: |
+ pushd src/python/build/lib.*
+ export PYTHONPATH=$PYTHONPATH:$(pwd)
+ popd
pushd tests/ftests
make check
popd
diff --git a/configure.ac b/configure.ac
index 2d10a527c1d4..84d7e688e4a7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -75,6 +75,21 @@ AC_ARG_ENABLE([daemon],
[with_daemon=true])
AM_CONDITIONAL([WITH_DAEMON], [test x$with_daemon = xtrue])
+AC_ARG_ENABLE([python],
+ [AS_HELP_STRING([--enable-python],
+ [build the python bindings, requires cython])])
+AS_IF([test "$enable_python" = yes], [
+ # cython version check
+ AS_IF([test "$CYTHON_VER_MAJ" -eq 0 -a "$CYTHON_VER_MIN" -lt 29], [
+ AC_MSG_ERROR([python bindings require cython 0.29 or higher])
+ ])
+ AM_PATH_PYTHON([3])
+])
+AM_CONDITIONAL([ENABLE_PYTHON], [test "$enable_python" = yes])
+AC_DEFINE_UNQUOTED([ENABLE_PYTHON],
+ [$(test "$enable_python" = yes && echo 1 || echo 0)],
+ [Python bindings build flag.])
+
AC_ARG_ENABLE([initscript-install],
[AS_HELP_STRING([--enable-initscript-install],[install init scripts
[default=no]])],
[
@@ -196,6 +211,7 @@ AC_CONFIG_FILES([Makefile
src/daemon/Makefile
src/tools/Makefile
src/pam/Makefile
+ src/python/Makefile
scripts/Makefile
scripts/init.d/cgconfig
scripts/init.d/cgred
diff --git a/src/Makefile.am b/src/Makefile.am
index 5227a0366072..176cf13c3333 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -3,6 +3,9 @@
BUILT_SOURCES = parse.c parse.h
SUBDIRS = . daemon pam tools
+if ENABLE_PYTHON
+SUBDIRS += python
+endif
# generate parse.h from parse.y
AM_YFLAGS = -d
diff --git a/src/python/.gitignore b/src/python/.gitignore
new file mode 100644
index 000000000000..8862a1395e13
--- /dev/null
+++ b/src/python/.gitignore
@@ -0,0 +1,6 @@
+build/
+Makefile
+Makefile.in
+__init__.py
+*.c
+*.swp
diff --git a/src/python/Makefile.am b/src/python/Makefile.am
new file mode 100644
index 000000000000..5c77add326bf
--- /dev/null
+++ b/src/python/Makefile.am
@@ -0,0 +1,56 @@
+#
+# Libcgroup Python Bindings
+#
+# Copyright (c) 2021 Oracle and/or its affiliates.
+# Author: Tom Hromatka <[email protected]>
+#
+
+#
+# This library is free software; you can redistribute it and/or modify it
+# under the terms of version 2.1 of the GNU Lesser General Public License as
+# published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+# for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, see <http://www.gnu.org/licenses>.
+#
+@CODE_COVERAGE_RULES@
+
+PY_DISTUTILS = \
+ VERSION_RELEASE="@PACKAGE_VERSION@" \
+ CPPFLAGS="-I\${top_srcdir}/include ${AM_CPPFLAGS} ${CPPFLAGS}" \
+ CFLAGS="$(CODE_COVERAGE_CFLAGS) ${AM_CFLAGS} ${CFLAGS}" \
+ LDFLAGS="$(CODE_COVERAGE_LIBS) ${AM_LDFLAGS} ${LDFLAGS}" \
+ ${PYTHON} ${srcdir}/setup.py
+
+# support silent builds
+PY_BUILD_0 = @echo " PYTHON " $@; ${PY_DISTUTILS} -q build
+PY_BUILD_1 = ${PY_DISTUTILS} build
+PY_BUILD_ = ${PY_BUILD_0}
+PY_BUILD = ${PY_BUILD_@AM_V@}
+
+PY_INSTALL = ${PY_DISTUTILS} install
+
+EXTRA_DIST = cgroup.pxd libcgroup.pyx setup.py
+
+all-local: build
+
+build: ../libcgroup.la cgroup.pxd libcgroup.pyx setup.py
+ [ ${srcdir} == ${builddir} ] || cp ${srcdir}/libcgroup.pyx ${builddir}
+ ${PY_BUILD} && touch build
+
+install-exec-local: build
+ ${PY_INSTALL} --install-lib=${DESTDIR}/${pyexecdir} \
+ --record=${DESTDIR}/${pyexecdir}/install_files.txt
+
+uninstall-local:
+ cat ${DESTDIR}/${pyexecdir}/install_files.txt | xargs ${RM} -f
+ ${RM} -f ${DESTDIR}/${pyexecdir}/install_files.txt
+
+clean-local:
+ [ ${srcdir} == ${builddir} ] || ${RM} -f ${builddir}/libcgroup.pyx
+ ${RM} -rf libcgroup.c build
diff --git a/src/python/cgroup.pxd b/src/python/cgroup.pxd
new file mode 100644
index 000000000000..82fa2af91a2d
--- /dev/null
+++ b/src/python/cgroup.pxd
@@ -0,0 +1,64 @@
+#
+# Libcgroup Python Bindings
+#
+# Copyright (c) 2021 Oracle and/or its affiliates.
+# Author: Tom Hromatka <[email protected]>
+#
+
+#
+# This library is free software; you can redistribute it and/or modify it
+# under the terms of version 2.1 of the GNU Lesser General Public License as
+# published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+# for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, see <http://www.gnu.org/licenses>.
+#
+
+# cython: language_level = 3str
+
+cdef extern from "libcgroup.h":
+ cdef struct cgroup:
+ pass
+
+ cdef struct cgroup_controller:
+ pass
+
+ cdef enum cg_version_t:
+ CGROUP_UNK
+ CGROUP_V1
+ CGROUP_V2
+ CGROUP_DISK
+
+ cdef struct cgroup_library_version:
+ unsigned int major
+ unsigned int minor
+ unsigned int release
+
+ int cgroup_init()
+ const cgroup_library_version * cgroup_version()
+
+ cgroup *cgroup_new_cgroup(const char *name)
+ int cgroup_convert_cgroup(cgroup *out_cg, cg_version_t out_version,
+ cgroup *in_cg, cg_version_t in_version)
+ void cgroup_free(cgroup **cg)
+
+ cgroup_controller *cgroup_add_controller(cgroup *cg, const char *name)
+ cgroup_controller *cgroup_get_controller(cgroup *cg, const char *name)
+
+ int cgroup_add_value_string(cgroup_controller *cgc, const char *name,
+ const char *value)
+ int cgroup_get_value_string(cgroup_controller *cgc, const char *name,
+ char **value)
+ char *cgroup_get_value_name(cgroup_controller *cgc, int index)
+ int cgroup_get_value_name_count(cgroup_controller *cgc)
+
+ int cgroup_cgxget(cgroup ** cg, cg_version_t version,
+ bint ignore_unmappable)
+
+ int cgroup_cgxset(const cgroup * const cg, cg_version_t version,
+ bint ignore_unmappable)
diff --git a/src/python/libcgroup.pyx b/src/python/libcgroup.pyx
new file mode 100644
index 000000000000..cf43a5ad905d
--- /dev/null
+++ b/src/python/libcgroup.pyx
@@ -0,0 +1,274 @@
+#
+# Libcgroup Python Bindings
+#
+# Copyright (c) 2021 Oracle and/or its affiliates.
+# Author: Tom Hromatka <[email protected]>
+#
+
+#
+# This library is free software; you can redistribute it and/or modify it
+# under the terms of version 2.1 of the GNU Lesser General Public License as
+# published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+# for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, see <http://www.gnu.org/licenses>.
+#
+
+# cython: language_level = 3str
+
+""" Python bindings for the libcgroup library
+"""
+
+__author__ = 'Tom Hromatka <[email protected]>'
+__date__ = "25 October 2021"
+
+cimport cgroup
+
+cdef class Version:
+ CGROUP_UNK = cgroup.CGROUP_UNK
+ CGROUP_V1 = cgroup.CGROUP_V1
+ CGROUP_V2 = cgroup.CGROUP_V2
+ CGROUP_DISK = cgroup.CGROUP_DISK
+
+def c_str(string):
+ return bytes(string, "ascii")
+
+def indent(in_str, cnt):
+ leading_indent = cnt * ' '
+ return ''.join(leading_indent + line for line in in_str.splitlines(True))
+
+class Controller:
+ def __init__(self, name):
+ self.name = name
+ # self.settings maps to
+ # struct control_value *values[CG_NV_MAX];
+ self.settings = dict()
+
+ def __str__(self):
+ out_str = "Controller {}\n".format(self.name)
+
+ for setting_key in self.settings:
+ out_str += indent("{} = {}\n".format(setting_key,
+ self.settings[setting_key]), 4)
+
+ return out_str
+
+cdef class Cgroup:
+ """ Python object representing a libcgroup cgroup """
+ cdef cgroup.cgroup * _cgp
+ cdef public:
+ object name, controllers, version
+
+ def __cinit__(self, name, version):
+ ret = cgroup.cgroup_init()
+ if ret != 0:
+ raise RuntimeError("Failed to initialize libcgroup:
{}".format(ret))
+
+ self._cgp = cgroup.cgroup_new_cgroup(c_str(name))
+ if self._cgp == NULL:
+ raise RuntimeError("Failed to create cgroup {}".format(name))
+
+ def __init__(self, name, version):
+ """Initialize this cgroup instance
+
+ Arguments:
+ name - Name of this cgroup
+ version - Version of this cgroup
+
+ Note:
+ Does not modify the cgroup sysfs. Does not read from the cgroup sysfs
+ """
+ self.name = name
+ self.controllers = dict()
+ self.version = version
+
+ def __str__(self):
+ out_str = "Cgroup {}\n".format(self.name)
+ for ctrl_key in self.controllers:
+ out_str += indent(str(self.controllers[ctrl_key]), 4)
+
+ return out_str
+
+ @staticmethod
+ def library_version():
+ cdef const cgroup.cgroup_library_version * version
+
+ version = cgroup.cgroup_version()
+ return [version.major, version.minor, version.release]
+
+ def add_controller(self, ctrl_name):
+ """Add a controller to the Cgroup instance
+
+ Arguments:
+ ctrl_name - name of the controller
+
+ Description:
+ Adds a controller to the Cgroup instance
+
+ Note:
+ Does not modify the cgroup sysfs
+ """
+ cdef cgroup.cgroup_controller * cgcp
+ cdef cgroup.cgroup * cgp
+
+ cgcp = cgroup.cgroup_add_controller(self._cgp,
+ c_str(ctrl_name))
+ if cgcp == NULL:
+ raise RuntimeError("Failed to add controller {} to cgroup".format(
+ ctrl_name))
+
+ self.controllers[ctrl_name] = Controller(ctrl_name)
+
+ def add_setting(self, setting_name, setting_value=None):
+ """Add a setting to the Cgroup/Controller instance
+
+ Arguments:
+ setting_name - name of the cgroup setting, e.g. 'cpu.shares'
+ setting_value (optional) - value
+
+ Description:
+ Adds a setting/value pair to the Cgroup/Controller instance
+
+ Note:
+ Does not modify the cgroup sysfs
+ """
+ cdef cgroup.cgroup_controller *cgcp
+ cdef char * value
+
+ ctrl_name = setting_name.split('.')[0]
+
+ cgcp = cgroup.cgroup_get_controller(self._cgp,
+ c_str(ctrl_name))
+ if cgcp == NULL:
+ self.add_controller(ctrl_name)
+
+ if setting_value == None:
+ ret = cgroup.cgroup_add_value_string(cgcp,
+ c_str(setting_name), NULL)
+ else:
+ ret = cgroup.cgroup_add_value_string(cgcp,
+ c_str(setting_name), c_str(setting_value))
+ if ret != 0:
+ raise RuntimeError("Failed to add setting {}: {}".format(
+ setting_name, ret))
+
+ def _pythonize_cgroup(self):
+ """
+ Given a populated self._cgp, populate the equivalent Python fields
+ """
+ cdef char *setting_name
+ cdef char *setting_value
+
+ for ctrlr_key in self.controllers:
+ cgcp = cgroup.cgroup_get_controller(self._cgp,
+ c_str(self.controllers[ctrlr_key].name))
+ if cgcp == NULL:
+ raise RuntimeError("Failed to get controller {}".format(
+ ctrlr_key))
+
+ self.controllers[ctrlr_key] = Controller(ctrlr_key)
+ setting_cnt = cgroup.cgroup_get_value_name_count(cgcp)
+
+ for i in range(0, setting_cnt):
+ setting_name = cgroup.cgroup_get_value_name(cgcp, i)
+
+ ret = cgroup.cgroup_get_value_string(cgcp,
+ setting_name, &setting_value)
+ if ret != 0:
+ raise RuntimeError("Failed to get value {}: {}".format(
+ setting_name, ret))
+
+ name = setting_name.decode("ascii")
+ value = setting_value.decode("ascii").strip()
+ self.controllers[ctrlr_key].settings[name] = value
+
+ def convert(self, out_version):
+ """Convert this cgroup to another cgroup version
+
+ Arguments:
+ out_version - Version to convert to
+
+ Return:
+ Returns the converted cgroup instance
+
+ Description:
+ Convert this cgroup instance to a cgroup instance of a different
+ cgroup version
+
+ Note:
+ Does not modify the cgroup sysfs. Does not read from the cgroup sysfs
+ """
+ out_cgp = Cgroup(self.name, out_version)
+ ret = cgroup.cgroup_convert_cgroup(out_cgp._cgp,
+ out_version, self._cgp, self.version)
+ if ret != 0:
+ raise RuntimeError("Failed to convert cgroup: {}".format(ret))
+
+ for ctrlr_key in self.controllers:
+ out_cgp.controllers[ctrlr_key] = Controller(ctrlr_key)
+
+ out_cgp._pythonize_cgroup()
+
+ return out_cgp
+
+ def cgxget(self, ignore_unmappable=False):
+ """Read the requested settings from the cgroup sysfs
+
+ Arguments:
+ ignore_unmappable - Ignore cgroup settings that can't be converted
+ from one version to another
+
+ Return:
+ Returns the cgroup instance that represents the settings read from
+ sysfs
+
+ Description:
+ Given this cgroup instance, read the settings/values from the
+ cgroup sysfs. If the read was successful, the settings are
+ returned in the return value
+
+ Note:
+ Reads from the cgroup sysfs
+ """
+ cdef bint ignore
+
+ if ignore_unmappable:
+ ignore = 1
+ else:
+ ignore = 0
+
+ ret = cgroup.cgroup_cgxget(&self._cgp, self.version, ignore)
+ if ret != 0:
+ raise RuntimeError("cgxget failed: {}".format(ret))
+
+ self._pythonize_cgroup()
+
+ def cgxset(self, ignore_unmappable=False):
+ """Write this cgroup to the cgroup sysfs
+
+ Arguments:
+ ignore_unmappable - Ignore cgroup settings that can't be converted
+ from one version to another
+
+ Description:
+ Write the settings/values in this cgroup instance to the cgroup sysfs
+
+ Note:
+ Writes to the cgroup sysfs
+ """
+ if ignore_unmappable:
+ ignore = 1
+ else:
+ ignore = 0
+
+ ret = cgroup.cgroup_cgxset(self._cgp, self.version, ignore)
+ if ret != 0:
+ raise RuntimeError("cgxset failed: {}".format(ret))
+
+ def __dealloc__(self):
+ cgroup.cgroup_free(&self._cgp);
diff --git a/src/python/setup.py b/src/python/setup.py
new file mode 100755
index 000000000000..f4b95d4a1a80
--- /dev/null
+++ b/src/python/setup.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+
+#
+# Libcgroup Python Module Build Script
+#
+
+#
+# Copyright (c) 2021 Oracle and/or its affiliates.
+# Author: Tom Hromatka <[email protected]>
+#
+
+#
+# This library is free software; you can redistribute it and/or modify it
+# under the terms of version 2.1 of the GNU Lesser General Public License as
+# published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+# for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, see <http://www.gnu.org/licenses>.
+#
+
+import os
+
+from distutils.core import setup
+from distutils.extension import Extension
+from Cython.Distutils import build_ext
+
+setup(
+ name = "libcgroup",
+ version = os.environ["VERSION_RELEASE"],
+ description = "Python bindings for libcgroup",
+ url = "https://github.com/libcgroup/libcgroup",
+ maintainer = "Tom Hromatka",
+ maintainer_email = "[email protected]",
+ license = "LGPLv2.1",
+ platforms = "Linux",
+ cmdclass = {'build_ext': build_ext},
+ ext_modules = [
+ Extension("libcgroup", ["libcgroup.pyx"],
+ # unable to handle libtool libraries directly
+ extra_objects=["../.libs/libcgroup.a"])
+ ]
+)
--
2.31.1
_______________________________________________
Libcg-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/libcg-devel