diff --git a/plugins/module_utils/module_container/base.py b/plugins/module_utils/module_container/base.py index 5662bb97f..f54db08af 100644 --- a/plugins/module_utils/module_container/base.py +++ b/plugins/module_utils/module_container/base.py @@ -175,7 +175,7 @@ class Engine(object): min_api_version_obj = None # LooseVersion object or None @abc.abstractmethod - def get_value(self, module, container, api_version, options, image): + def get_value(self, module, container, api_version, options, image, host_info): pass def compare_value(self, option, param_value, container_value): @@ -186,7 +186,7 @@ def set_value(self, module, data, api_version, options, values): pass @abc.abstractmethod - def get_expected_values(self, module, client, api_version, options, image, values): + def get_expected_values(self, module, client, api_version, options, image, values, host_info): pass @abc.abstractmethod @@ -213,6 +213,10 @@ def can_update_value(self, api_version): def needs_container_image(self, values): pass + @abc.abstractmethod + def needs_host_info(self, values): + pass + class EngineDriver(object): name = None # string @@ -222,6 +226,10 @@ def setup(self, argument_spec, mutually_exclusive=None, required_together=None, # Return (module, active_options, client) pass + @abc.abstractmethod + def get_host_info(self, client): + pass + @abc.abstractmethod def get_api_version(self, client): pass diff --git a/plugins/module_utils/module_container/docker_api.py b/plugins/module_utils/module_container/docker_api.py index 6aad6d358..b0e64a07e 100644 --- a/plugins/module_utils/module_container/docker_api.py +++ b/plugins/module_utils/module_container/docker_api.py @@ -181,6 +181,9 @@ def setup(self, argument_spec, mutually_exclusive=None, required_together=None, return client.module, active_options, client + def get_host_info(self, client): + return client.info() + def get_api_version(self, client): return client.docker_api_version @@ -389,12 +392,13 @@ def __init__( min_api_version=None, compare_value=None, needs_container_image=None, + needs_host_info=None, ): self.min_api_version = min_api_version self.min_api_version_obj = None if min_api_version is None else LooseVersion(min_api_version) self.get_value = get_value self.set_value = set_value - self.get_expected_values = get_expected_values or (lambda module, client, api_version, options, image, values: values) + self.get_expected_values = get_expected_values or (lambda module, client, api_version, options, image, values, host_info: values) self.ignore_mismatching_result = ignore_mismatching_result or \ (lambda module, client, api_version, option, image, container_value, expected_value: False) self.preprocess_value = preprocess_value or (lambda module, client, api_version, options, values: values) @@ -402,6 +406,7 @@ def __init__( self.can_set_value = can_set_value or (lambda api_version: set_value is not None) self.can_update_value = can_update_value or (lambda api_version: update_value is not None) self.needs_container_image = needs_container_image or (lambda values: False) + self.needs_host_info = needs_host_info or (lambda values: False) if compare_value is not None: self.compare_value = compare_value @@ -428,7 +433,7 @@ def preprocess_value_(module, client, api_version, options, values): values[options[0].name] = value return values - def get_value(module, container, api_version, options, image): + def get_value(module, container, api_version, options, image, host_info): if len(options) != 1: raise AssertionError('config_value can only be used for a single option') value = container['Config'].get(config_name, _SENTRY) @@ -440,7 +445,7 @@ def get_value(module, container, api_version, options, image): get_expected_values_ = None if get_expected_value: - def get_expected_values_(module, client, api_version, options, image, values): + def get_expected_values_(module, client, api_version, options, image, values, host_info): if len(options) != 1: raise AssertionError('host_config_value can only be used for a single option') value = values.get(options[0].name, _SENTRY) @@ -504,7 +509,7 @@ def preprocess_value_(module, client, api_version, options, values): values[options[0].name] = value return values - def get_value(module, container, api_version, options, get_value): + def get_value(module, container, api_version, options, get_value, host_info): if len(options) != 1: raise AssertionError('host_config_value can only be used for a single option') value = container['HostConfig'].get(host_config_name, _SENTRY) @@ -516,7 +521,7 @@ def get_value(module, container, api_version, options, get_value): get_expected_values_ = None if get_expected_value: - def get_expected_values_(module, client, api_version, options, image, values): + def get_expected_values_(module, client, api_version, options, image, values, host_info): if len(options) != 1: raise AssertionError('host_config_value can only be used for a single option') value = values.get(options[0].name, _SENTRY) @@ -590,7 +595,7 @@ def _get_default_host_ip(module, client): return ip -def _get_value_detach_interactive(module, container, api_version, options, image): +def _get_value_detach_interactive(module, container, api_version, options, image, host_info): attach_stdin = container['Config'].get('OpenStdin') attach_stderr = container['Config'].get('AttachStderr') attach_stdout = container['Config'].get('AttachStdout') @@ -841,7 +846,7 @@ def _get_network_id(module, client, network_name): client.fail("Error getting network id for %s - %s" % (network_name, to_native(exc))) -def _get_values_network(module, container, api_version, options, image): +def _get_values_network(module, container, api_version, options, image, host_info): value = container['HostConfig'].get('NetworkMode', _SENTRY) if value is _SENTRY: return {} @@ -857,7 +862,7 @@ def _set_values_network(module, data, api_version, options, values): data['HostConfig']['NetworkMode'] = value -def _get_values_mounts(module, container, api_version, options, image): +def _get_values_mounts(module, container, api_version, options, image, host_info): volumes = container['Config'].get('Volumes') binds = container['HostConfig'].get('Binds') # According to https://github.com/moby/moby/, support for HostConfig.Mounts @@ -921,7 +926,7 @@ def _get_image_binds(volumes): return results -def _get_expected_values_mounts(module, client, api_version, options, image, values): +def _get_expected_values_mounts(module, client, api_version, options, image, values, host_info): expected_values = {} # binds @@ -1022,7 +1027,7 @@ def _set_values_mounts(module, data, api_version, options, values): data['HostConfig']['Binds'] = values['volume_binds'] -def _get_values_log(module, container, api_version, options, image): +def _get_values_log(module, container, api_version, options, image, host_info): log_config = container['HostConfig'].get('LogConfig') or {} return { 'log_driver': log_config.get('Type'), @@ -1042,7 +1047,7 @@ def _set_values_log(module, data, api_version, options, values): data['HostConfig']['LogConfig'] = log_config -def _get_values_platform(module, container, api_version, options, image): +def _get_values_platform(module, container, api_version, options, image, host_info): return { 'platform': container.get('Platform'), } @@ -1053,7 +1058,7 @@ def _set_values_platform(module, data, api_version, options, values): data['platform'] = values['platform'] -def _get_values_restart(module, container, api_version, options, image): +def _get_values_restart(module, container, api_version, options, image, host_info): restart_policy = container['HostConfig'].get('RestartPolicy') or {} return { 'restart_policy': restart_policy.get('Name'), @@ -1082,7 +1087,7 @@ def _update_value_restart(module, data, api_version, options, values): } -def _get_values_ports(module, container, api_version, options, image): +def _get_values_ports(module, container, api_version, options, image, host_info): host_config = container['HostConfig'] config = container['Config'] @@ -1099,7 +1104,7 @@ def _get_values_ports(module, container, api_version, options, image): } -def _get_expected_values_ports(module, client, api_version, options, image, values): +def _get_expected_values_ports(module, client, api_version, options, image, values, host_info): expected_values = {} if 'published_ports' in values: diff --git a/plugins/module_utils/module_container/module.py b/plugins/module_utils/module_container/module.py index 6865aef8c..8f76ed7a4 100644 --- a/plugins/module_utils/module_container/module.py +++ b/plugins/module_utils/module_container/module.py @@ -275,6 +275,13 @@ def _needs_container_image(self): return True return False + def _needs_host_info(self): + for options, values in self.parameters: + engine = options.get_engine(self.engine_driver.name) + if engine.needs_host_info(values): + return True + return False + def present(self, state): self.parameters = self._collect_params(self.options) container = self._get_container(self.param_name) @@ -290,6 +297,7 @@ def present(self, state): image, container_image, comparison_image = self._get_image( container, needs_container_image=self._needs_container_image()) self.log(image, pretty_print=True) + host_info = self.engine_driver.get_host_info(self.client) if self._needs_host_info() else None if not container.exists or container.removing: # New container if container.removing: @@ -309,7 +317,7 @@ def present(self, state): container_created = True else: # Existing container - different, differences = self.has_different_configuration(container, container_image, comparison_image) + different, differences = self.has_different_configuration(container, container_image, comparison_image, host_info) image_different = False if self.all_options['image'].comparison == 'strict': image_different = self._image_is_different(image, container) @@ -341,7 +349,7 @@ def present(self, state): comparison_image = image if container and container.exists: - container = self.update_limits(container, container_image, comparison_image) + container = self.update_limits(container, container_image, comparison_image, host_info) container = self.update_networks(container, container_created) if state == 'started' and not container.running: @@ -469,11 +477,11 @@ def _compose_create_parameters(self, image): params['Image'] = image return params - def _record_differences(self, differences, options, param_values, engine, container, container_image, image): + def _record_differences(self, differences, options, param_values, engine, container, container_image, image, host_info): container_values = engine.get_value( - self.module, container.raw, self.engine_driver.get_api_version(self.client), options.options, container_image) + self.module, container.raw, self.engine_driver.get_api_version(self.client), options.options, container_image, host_info) expected_values = engine.get_expected_values( - self.module, self.client, self.engine_driver.get_api_version(self.client), options.options, image, param_values.copy()) + self.module, self.client, self.engine_driver.get_api_version(self.client), options.options, image, param_values.copy(), host_info) for option in options.options: if option.name in expected_values: param_value = expected_values[option.name] @@ -512,28 +520,28 @@ def sort_key_fn(x): c = sorted(c, key=sort_key_fn) differences.add(option.name, parameter=p, active=c) - def has_different_configuration(self, container, container_image, image): + def has_different_configuration(self, container, container_image, image, host_info): differences = DifferenceTracker() update_differences = DifferenceTracker() for options, param_values in self.parameters: engine = options.get_engine(self.engine_driver.name) if engine.can_update_value(self.engine_driver.get_api_version(self.client)): - self._record_differences(update_differences, options, param_values, engine, container, container_image, image) + self._record_differences(update_differences, options, param_values, engine, container, container_image, image, host_info) else: - self._record_differences(differences, options, param_values, engine, container, container_image, image) + self._record_differences(differences, options, param_values, engine, container, container_image, image, host_info) has_differences = not differences.empty # Only consider differences of properties that can be updated when there are also other differences if has_differences: differences.merge(update_differences) return has_differences, differences - def has_different_resource_limits(self, container, container_image, image): + def has_different_resource_limits(self, container, container_image, image, host_info): differences = DifferenceTracker() for options, param_values in self.parameters: engine = options.get_engine(self.engine_driver.name) if not engine.can_update_value(self.engine_driver.get_api_version(self.client)): continue - self._record_differences(differences, options, param_values, engine, container, container_image, image) + self._record_differences(differences, options, param_values, engine, container, container_image, image, host_info) has_differences = not differences.empty return has_differences, differences @@ -546,8 +554,8 @@ def _compose_update_parameters(self): engine.update_value(self.module, result, self.engine_driver.get_api_version(self.client), options.options, values) return result - def update_limits(self, container, container_image, image): - limits_differ, different_limits = self.has_different_resource_limits(container, container_image, image) + def update_limits(self, container, container_image, image, host_info): + limits_differ, different_limits = self.has_different_resource_limits(container, container_image, image, host_info) if limits_differ: self.log("limit differences:") self.log(different_limits.get_legacy_docker_container_diffs(), pretty_print=True)