diff --git a/docker/api/container.py b/docker/api/container.py index 85e5e90a..97b54059 100644 --- a/docker/api/container.py +++ b/docker/api/container.py @@ -479,6 +479,8 @@ class ContainerApiMixin(object): cpu_shares (int): CPU shares (relative weight). cpuset_cpus (str): CPUs in which to allow execution (``0-3``, ``0,1``). + cpuset_mems (str): Memory nodes (MEMs) in which to allow execution + (``0-3``, ``0,1``). Only effective on NUMA systems. device_read_bps: Limit read rate (bytes per second) from a device in the form of: `[{"Path": "device_path", "Rate": rate}]` device_read_iops: Limit read rate (IO per second) from a device. diff --git a/docker/models/containers.py b/docker/models/containers.py index f1c0fd26..2213583a 100644 --- a/docker/models/containers.py +++ b/docker/models/containers.py @@ -483,6 +483,8 @@ class ContainerCollection(Collection): cpu_shares (int): CPU shares (relative weight). cpuset_cpus (str): CPUs in which to allow execution (``0-3``, ``0,1``). + cpuset_mems (str): Memory nodes (MEMs) in which to allow execution + (``0-3``, ``0,1``). Only effective on NUMA systems. detach (bool): Run container in the background and return a :py:class:`Container` object. device_read_bps: Limit read rate (bytes per second) from a device @@ -829,6 +831,7 @@ RUN_HOST_CONFIG_KWARGS = [ 'cpu_quota', 'cpu_shares', 'cpuset_cpus', + 'cpuset_mems', 'device_read_bps', 'device_read_iops', 'device_write_bps', diff --git a/docker/types/containers.py b/docker/types/containers.py index 9f1a04d1..ad162555 100644 --- a/docker/types/containers.py +++ b/docker/types/containers.py @@ -119,7 +119,8 @@ class HostConfig(dict): cpuset_cpus=None, userns_mode=None, pids_limit=None, isolation=None, auto_remove=False, storage_opt=None, init=None, init_path=None, volume_driver=None, - cpu_count=None, cpu_percent=None, nano_cpus=None): + cpu_count=None, cpu_percent=None, nano_cpus=None, + cpuset_mems=None): if mem_limit is not None: self['Memory'] = parse_bytes(mem_limit) @@ -328,6 +329,16 @@ class HostConfig(dict): self['CpuSetCpus'] = cpuset_cpus + if cpuset_mems: + if version_lt(version, '1.19'): + raise host_config_version_error('cpuset_mems', '1.19') + + if not isinstance(cpuset_mems, str): + raise host_config_type_error( + 'cpuset_mems', cpuset_mems, 'str' + ) + self['CpusetMems'] = cpuset_mems + if blkio_weight: if not isinstance(blkio_weight, int): raise host_config_type_error( diff --git a/tests/unit/api_container_test.py b/tests/unit/api_container_test.py index 901934ee..39df2939 100644 --- a/tests/unit/api_container_test.py +++ b/tests/unit/api_container_test.py @@ -338,6 +338,33 @@ class CreateContainerTest(BaseAPIClientTest): self.assertEqual(args[1]['headers'], {'Content-Type': 'application/json'}) + @requires_api_version('1.19') + def test_create_container_with_host_config_cpuset_mems(self): + self.client.create_container( + 'busybox', 'ls', host_config=self.client.create_host_config( + cpuset_mems='0' + ) + ) + + args = fake_request.call_args + self.assertEqual(args[0][1], + url_prefix + 'containers/create') + + self.assertEqual(json.loads(args[1]['data']), + json.loads(''' + {"Tty": false, "Image": "busybox", + "Cmd": ["ls"], "AttachStdin": false, + "AttachStderr": true, + "AttachStdout": true, "OpenStdin": false, + "StdinOnce": false, + "NetworkDisabled": false, + "HostConfig": { + "CpusetMems": "0", + "NetworkMode": "default" + }}''')) + self.assertEqual(args[1]['headers'], + {'Content-Type': 'application/json'}) + def test_create_container_with_cgroup_parent(self): self.client.create_container( 'busybox', 'ls', host_config=self.client.create_host_config(