diff --git a/ansible/library/kolla_docker.py b/ansible/library/kolla_docker.py index 1c8cc2bb8a..38c0900771 100644 --- a/ansible/library/kolla_docker.py +++ b/ansible/library/kolla_docker.py @@ -474,7 +474,8 @@ class DockerWorker(object): 'memswap_limit': 'MemorySwap', 'cpu_period': 'CpuPeriod', 'cpu_quota': 'CpuQuota', 'cpu_shares': 'CpuShares', 'cpuset_cpus': 'CpusetCpus', 'cpuset_mems': 'CpusetMems', - 'kernel_memory': 'KernelMemory', 'blkio_weight': 'BlkioWeight'} + 'kernel_memory': 'KernelMemory', 'blkio_weight': 'BlkioWeight', + 'ulimits': 'Ulimits'} unsupported = set(new_dimensions.keys()) - \ set(dimension_map.keys()) if unsupported: @@ -486,13 +487,29 @@ class DockerWorker(object): # NOTE(mgoddard): If a resource has been explicitly requested, # check for a match. Otherwise, ensure is is set to the default. if key1 in new_dimensions: - if new_dimensions[key1] != current_dimensions[key2]: + if key1 == 'ulimits': + if self.compare_ulimits(new_dimensions[key1], + current_dimensions[key2]): + return True + elif new_dimensions[key1] != current_dimensions[key2]: return True elif current_dimensions[key2]: # The default values of all currently supported resources are # '' or 0 - both falsey. return True + def compare_ulimits(self, new_ulimits, current_ulimits): + # The new_ulimits is dict, we need make it to a list of Ulimit + # instance. + new_ulimits = self.build_ulimits(new_ulimits) + + def key(ulimit): + return ulimit['Name'] + + if current_ulimits is None: + current_ulimits = [] + return sorted(new_ulimits, key=key) != sorted(current_ulimits, key=key) + def compare_command(self, container_info): new_command = self.params.get('command') if new_command is not None: @@ -606,6 +623,40 @@ class DockerWorker(object): return vol_list, vol_dict + def parse_dimensions(self, dimensions): + # When the data object contains types such as + # docker.types.Ulimit, Ansible will fail when these are + # returned via exit_json or fail_json. HostConfig is derived from dict, + # but its constructor requires additional arguments. + # to avoid that, here do copy the dimensions and return a new one. + dimensions = dimensions.copy() + + supported = {'cpu_period', 'cpu_quota', 'cpu_shares', + 'cpuset_cpus', 'cpuset_mems', 'mem_limit', + 'mem_reservation', 'memswap_limit', + 'kernel_memory', 'blkio_weight', 'ulimits'} + unsupported = set(dimensions) - supported + if unsupported: + self.module.exit_json(failed=True, + msg=repr("Unsupported dimensions"), + unsupported_dimensions=unsupported) + + ulimits = dimensions.get('ulimits') + if ulimits: + dimensions['ulimits'] = self.build_ulimits(ulimits) + + return dimensions + + def build_ulimits(self, ulimits): + ulimits_opt = [] + for key, value in ulimits.items(): + soft = value.get('soft') + hard = value.get('hard') + ulimits_opt.append(docker.types.Ulimit(name=key, + soft=soft, + hard=hard)) + return ulimits_opt + def build_host_config(self, binds): options = { 'network_mode': 'host', @@ -617,17 +668,11 @@ class DockerWorker(object): 'volumes_from': self.params.get('volumes_from') } - if self.params.get('dimensions'): - supported = {'cpu_period', 'cpu_quota', 'cpu_shares', - 'cpuset_cpus', 'cpuset_mems', 'mem_limit', - 'mem_reservation', 'memswap_limit', - 'kernel_memory', 'blkio_weight'} - unsupported = set(self.params.get('dimensions')) - supported - if unsupported: - self.module.exit_json(failed=True, - msg=repr("Unsupported dimensions"), - unsupported_dimensions=unsupported) - options.update(self.params.get('dimensions')) + dimensions = self.params.get('dimensions') + + if dimensions: + dimensions = self.parse_dimensions(dimensions) + options.update(dimensions) if self.params.get('restart_policy') in ['on-failure', 'always', @@ -940,5 +985,6 @@ def main(): module.fail_json(changed=True, msg=repr(traceback.format_exc()), **dw.result) + if __name__ == '__main__': main() diff --git a/doc/source/reference/deployment-config/resource-constraints.rst b/doc/source/reference/deployment-config/resource-constraints.rst index 4413b59920..4b15c00ec0 100644 --- a/doc/source/reference/deployment-config/resource-constraints.rst +++ b/doc/source/reference/deployment-config/resource-constraints.rst @@ -27,6 +27,7 @@ The resources currently supported by Kolla Ansible are: memswap_limit kernel_memory blkio_weight + ulimits Pre-deployment Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -67,6 +68,9 @@ Dimensions are defined as a mapping from a Docker resource name * - cpuset_mems - String - ''(Empty String) + * - ulimits + - Dict + - {} The variable ``default_container_dimensions`` sets the default dimensions @@ -96,6 +100,23 @@ options section in ``/etc/kolla/globals.yml``: nova_libvirt_dimensions: cpuset_cpus: "2" +How to config ulimits in kolla +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: yaml + + _dimensions: + ulimits: + nofile: + soft: 131072 + hard: 131072 + fsize: + soft: 131072 + hard: 131072 + +A list of valid names can be found [here] +(https://github.com/docker/go-units/blob/d4a9b9617350c034730bc5051c605919943080bf/ulimit.go#L46-L63) + Deployment ~~~~~~~~~~ diff --git a/etc/kolla/globals.yml b/etc/kolla/globals.yml index 83e8cfc0ab..5686aad338 100644 --- a/etc/kolla/globals.yml +++ b/etc/kolla/globals.yml @@ -142,6 +142,7 @@ kolla_internal_vip_address: "10.10.10.254" # mem_reservation: # memswap_limit: # kernel_memory: +# ulimits: ############# diff --git a/releasenotes/notes/add_ulimit_support-35e8799f29a44d12.yaml b/releasenotes/notes/add_ulimit_support-35e8799f29a44d12.yaml new file mode 100644 index 0000000000..f49e2a1118 --- /dev/null +++ b/releasenotes/notes/add_ulimit_support-35e8799f29a44d12.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ulimit support for kolla_docker which provides ability + of modify docker ulimits。 diff --git a/tests/test_kolla_docker.py b/tests/test_kolla_docker.py index ff0bee9eec..1a7d943529 100644 --- a/tests/test_kolla_docker.py +++ b/tests/test_kolla_docker.py @@ -23,6 +23,7 @@ try: except ImportError: import mock from docker import errors as docker_error +from docker.types import Ulimit from oslotest import base this_dir = os.path.dirname(sys.modules[__name__].__file__) @@ -904,7 +905,8 @@ class TestAttrComp(base.BaseTestCase): container_info['HostConfig'] = { 'CpuPeriod': 0, 'KernelMemory': 0, 'Memory': 0, 'CpuQuota': 0, 'CpusetCpus': '', 'CpuShares': 0, 'BlkioWeight': 0, - 'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0} + 'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0, + 'Ulimits': []} self.dw = get_DockerWorker(self.fake_data['params']) self.assertTrue(self.dw.compare_dimensions(container_info)) @@ -915,7 +917,8 @@ class TestAttrComp(base.BaseTestCase): container_info['HostConfig'] = { 'CpuPeriod': 0, 'KernelMemory': 0, 'Memory': 0, 'CpuQuota': 0, 'CpusetCpus': '', 'CpuShares': 0, 'BlkioWeight': 10, - 'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0} + 'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0, + 'Ulimits': []} self.dw = get_DockerWorker(self.fake_data['params']) self.assertFalse(self.dw.compare_dimensions(container_info)) @@ -926,7 +929,8 @@ class TestAttrComp(base.BaseTestCase): container_info['HostConfig'] = { 'CpuPeriod': 0, 'KernelMemory': 0, 'Memory': 0, 'CpuQuota': 0, 'CpusetCpus': '', 'CpuShares': 0, 'BlkioWeight': 0, - 'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0} + 'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0, + 'Ulimits': []} self.dw = get_DockerWorker(self.fake_data['params']) self.dw.compare_dimensions(container_info) self.dw.module.exit_json.assert_called_once_with( @@ -939,7 +943,8 @@ class TestAttrComp(base.BaseTestCase): container_info['HostConfig'] = { 'CpuPeriod': 0, 'KernelMemory': 0, 'Memory': 0, 'CpuQuota': 0, 'CpusetCpus': '1', 'CpuShares': 0, 'BlkioWeight': 0, - 'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0} + 'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0, + 'Ulimits': []} self.dw = get_DockerWorker(self.fake_data['params']) self.assertTrue(self.dw.compare_dimensions(container_info)) @@ -954,7 +959,8 @@ class TestAttrComp(base.BaseTestCase): container_info['HostConfig'] = { 'CpuPeriod': 0, 'KernelMemory': 0, 'Memory': 10, 'CpuQuota': 0, 'CpusetCpus': '', 'CpuShares': 0, 'BlkioWeight': 0, - 'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 10} + 'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 10, + 'Ulimits': []} self.dw = get_DockerWorker(self.fake_data['params']) self.assertTrue(self.dw.compare_dimensions(container_info)) @@ -969,7 +975,8 @@ class TestAttrComp(base.BaseTestCase): container_info['HostConfig'] = { 'CpuPeriod': 0, 'KernelMemory': 0, 'Memory': 0, 'CpuQuota': 0, 'CpusetCpus': '', 'CpuShares': 0, 'BlkioWeight': 0, - 'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0} + 'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0, + 'Ulimits': []} self.dw = get_DockerWorker(self.fake_data['params']) self.assertFalse(self.dw.compare_dimensions(container_info)) @@ -977,3 +984,29 @@ class TestAttrComp(base.BaseTestCase): container_info = {'State': dict(Status='running')} self.dw = get_DockerWorker({'state': 'exited'}) self.assertTrue(self.dw.compare_container_state(container_info)) + + def test_compare_ulimits_pos(self): + self.fake_data['params']['dimensions'] = { + 'ulimits': {'nofile': {'soft': 131072, 'hard': 131072}}} + container_info = dict() + container_info['HostConfig'] = { + 'CpuPeriod': 0, 'KernelMemory': 0, 'Memory': 0, 'CpuQuota': 0, + 'CpusetCpus': '', 'CpuShares': 0, 'BlkioWeight': 0, + 'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0, + 'Ulimits': []} + self.dw = get_DockerWorker(self.fake_data['params']) + self.assertTrue(self.dw.compare_dimensions(container_info)) + + def test_compare_ulimits_neg(self): + self.fake_data['params']['dimensions'] = { + 'ulimits': {'nofile': {'soft': 131072, 'hard': 131072}}} + ulimits_nofile = Ulimit(name='nofile', + soft=131072, hard=131072) + container_info = dict() + container_info['HostConfig'] = { + 'CpuPeriod': 0, 'KernelMemory': 0, 'Memory': 0, 'CpuQuota': 0, + 'CpusetCpus': '', 'CpuShares': 0, 'BlkioWeight': 0, + 'CpusetMems': '', 'MemorySwap': 0, 'MemoryReservation': 0, + 'Ulimits': [ulimits_nofile]} + self.dw = get_DockerWorker(self.fake_data['params']) + self.assertFalse(self.dw.compare_dimensions(container_info))