diff --git a/awx_collection/README.md b/awx_collection/README.md index 2ddaf994b5..a5d438ba45 100644 --- a/awx_collection/README.md +++ b/awx_collection/README.md @@ -22,7 +22,8 @@ The following notes are changes that may require changes to playbooks. - Creating a "scan" type job template is no longer supported. - `extra_vars` in the `tower_job_launch` module worked with a list previously, but is now configured to work solely in a `dict` format. - When the `extra_vars` parameter is used with the `tower_job_launch` module, the Job Template launch will fail unless `add_extra_vars` or `survey_enabled` is explicitly set to `True` on the Job Template. - - tower_group used to also service inventory sources. tower_inventory_source has been split out into its own module. + - tower_group used to also service inventory sources, this functionality has been removed from this module; instead use tower_inventory_source. + - Specified tower_config file used to handle k=v pairs on a single line. This is no longer supported. You may a file formatted in: yaml, json or ini only. ## Running diff --git a/awx_collection/plugins/module_utils/tower_api.py b/awx_collection/plugins/module_utils/tower_api.py index 0a258c6af5..7c61e16928 100644 --- a/awx_collection/plugins/module_utils/tower_api.py +++ b/awx_collection/plugins/module_utils/tower_api.py @@ -8,7 +8,7 @@ from ansible.module_utils.six.moves import StringIO from ansible.module_utils.six.moves.urllib.parse import urlparse, urlencode from ansible.module_utils.six.moves.urllib.error import HTTPError from ansible.module_utils.six.moves.http_cookiejar import CookieJar -from ansible.module_utils.six.moves.configparser import ConfigParser, NoOptionError, MissingSectionHeaderError +from ansible.module_utils.six.moves.configparser import ConfigParser, NoOptionError from socket import gethostbyname import re from json import loads, dumps @@ -131,7 +131,6 @@ class TowerModule(AnsibleModule): self.fail_json(msg=cfe) def load_config(self, config_path): - config = ConfigParser() # Validate the config file is an actual file if not isfile(config_path): raise ConfigFileException('The specified config file does not exist') @@ -139,27 +138,55 @@ class TowerModule(AnsibleModule): if not access(config_path, R_OK): raise ConfigFileException("The specified config file can not be read") - # If the config has no sections we will get a MissingSectionHeaderError - try: - config.read(config_path) - except MissingSectionHeaderError: - with open(config_path, 'r') as f: - config_string = '[general]\n%s' % f.read() - placeholder_file = StringIO(config_string) - if hasattr(config, 'read_file'): - config.read_file(placeholder_file) - else: - config.readfp(placeholder_file) + # Read in the file contents: + with open(config_path, 'r') as f: + config_string = f.read() + + # First try to yaml load the content (which will also load json) + try: + config_data = yaml.load(config_string, Loader=yaml.SafeLoader) + # If this is an actual ini file, yaml will return the whole thing as a string instead of a dict + if type(config_data) is not dict: + raise AssertionError("The yaml config file is not properly formatted as a dict.") + + except(AttributeError, yaml.YAMLError, AssertionError): + # TowerCLI used to support a config file with a missing [general] section by prepending it if missing + if '[general]' not in config_string: + config_string = '[general]{0}'.format(config_string) + + config = ConfigParser() - for honorred_setting in self.honorred_settings: try: - setattr(self, honorred_setting, config.get('general', honorred_setting)) - if honorred_setting == 'verify_ssl': - setattr(self, honorred_setting, strtobool(config.get('general', honorred_setting))) + placeholder_file = StringIO(config_string) + # py2 ConfigParser has readfp, that has been deprecated in favor of read_file in py3 + # This "if" removes the deprecation warning + if hasattr(config, 'read_file'): + config.read_file(placeholder_file) else: - setattr(self, honorred_setting, config.get('general', honorred_setting)) - except (NoOptionError): - pass + config.readfp(placeholder_file) + + # If we made it here then we have values from reading the ini file, so lets pull them out into a dict + config_data = {} + for honorred_setting in self.honorred_settings: + try: + config_data[honorred_setting] = config.get('general', honorred_setting) + except (NoOptionError): + pass + + except Exception as e: + raise ConfigFileException("An unknown exception occured trying to ini load config file: {0}".format(e)) + + except Exception as e: + raise ConfigFileException("An unknown exception occured trying to load config file: {0}".format(e)) + + # If we made it here, we have a dict which has values in it from our config, any final settings logic can be performed here + for honorred_setting in self.honorred_settings: + if honorred_setting in config_data: + # Veriffy SSL must be a boolean + if honorred_setting == 'verify_ssl': + setattr(self, honorred_setting, strtobool(config_data[honorred_setting])) + else: + setattr(self, honorred_setting, config_data[honorred_setting]) def head_endpoint(self, endpoint, *args, **kwargs): return self.make_request('HEAD', endpoint, **kwargs) diff --git a/awx_collection/tests/sanity/ignore-2.9.txt b/awx_collection/tests/sanity/ignore-2.9.txt index fa05535904..c9df9574f5 100644 --- a/awx_collection/tests/sanity/ignore-2.9.txt +++ b/awx_collection/tests/sanity/ignore-2.9.txt @@ -1,2 +1 @@ -plugins/modules/tower_group.py use-argspec-type-path -plugins/modules/tower_host.py use-argspec-type-path \ No newline at end of file +plugins/modules/tower_host.py use-argspec-type-path