def check_supported_ocp_version(self, hostvars, host, openshift_deployment_type): """Checks that the OCP version supported""" if openshift_deployment_type == 'origin': return None openshift_version = self.template_var(hostvars, host, 'openshift_version') for regex_to_match, error_msg in UNSUPPORTED_OCP_VERSIONS.items(): res = re.match(regex_to_match, str(openshift_version)) if res is not None: raise errors.AnsibleModuleError(error_msg) return None
def check_pkg_version_format(self, hostvars, host): """Ensure openshift_pkg_version is formatted correctly""" openshift_pkg_version = self.template_var(hostvars, host, 'openshift_pkg_version') if not openshift_pkg_version: return None regex_to_match = PKG_VERSION_REGEX['re'] res = re.match(regex_to_match, str(openshift_pkg_version)) if res is None: msg = PKG_VERSION_REGEX['error_msg'] msg = msg.format(str(openshift_pkg_version)) raise errors.AnsibleModuleError(msg)
def check_release_format(self, hostvars, host): """Ensure openshift_release is formatted correctly""" openshift_release = self.template_var(hostvars, host, 'openshift_release') if not openshift_release: return None regex_to_match = RELEASE_REGEX['re'] res = re.match(regex_to_match, str(openshift_release)) if res is None: msg = RELEASE_REGEX['error_msg'] msg = msg.format(str(openshift_release)) raise errors.AnsibleModuleError(msg)
def get_allowed_registries(self, hostvars, host): """Returns a list of configured allowedRegistriesForImport as a list of patterns""" allowed_registries_for_import = self.template_var(hostvars, host, ALLOWED_REGISTRIES_VAR) if allowed_registries_for_import is None: image_policy_config = self.template_var(hostvars, host, IMAGE_POLICY_CONFIG_VAR) if not image_policy_config: return image_policy_config if isinstance(image_policy_config, str): try: image_policy_config = json.loads(image_policy_config) except Exception: raise errors.AnsibleModuleError( "{} is not a valid json string".format(IMAGE_POLICY_CONFIG_VAR)) if not isinstance(image_policy_config, dict): raise errors.AnsibleModuleError( "expected dictionary for {}, not {}".format( IMAGE_POLICY_CONFIG_VAR, type(image_policy_config))) detailed = image_policy_config.get("allowedRegistriesForImport", None) if not detailed: return detailed if not isinstance(detailed, list): raise errors.AnsibleModuleError("expected list for {}['{}'], not {}".format( IMAGE_POLICY_CONFIG_VAR, "allowedRegistriesForImport", type(allowed_registries_for_import))) try: return [i["domainName"] for i in detailed] except Exception: raise errors.AnsibleModuleError( "each item of allowedRegistriesForImport must be a dictionary with 'domainName' key") if not isinstance(allowed_registries_for_import, list): raise errors.AnsibleModuleError("expected list for {}, not {}".format( IMAGE_POLICY_CONFIG_VAR, type(allowed_registries_for_import))) return allowed_registries_for_import
def return_config_overrides_ini(self, config_overrides, resultant): """Returns string value from a modified config file. :param config_overrides: ``dict`` :param resultant: ``str`` || ``unicode`` :returns: ``str`` """ # If there is an exception loading the RawConfigParser The config obj # is loaded again without the extra option. This is being done to # support older python. try: config = ConfigTemplateParser(allow_no_value=True, dict_type=MultiKeyDict) config.optionxform = str except Exception: config = ConfigTemplateParser(dict_type=MultiKeyDict) config_object = io.BytesIO(str(resultant)) config.readfp(config_object) for section, items in config_overrides.items(): # If the items value is not a dictionary it is assumed that the # value is a default item for this config type. if not isinstance(items, dict): if isinstance(items, list): items = ','.join(_convert_2_string(items)) self._option_write(config, 'DEFAULT', str(section), items) else: # Attempt to add a section to the config file passing if # an error is raised that is related to the section # already existing. try: config.add_section(section.encode('utf-8')) except (ConfigParser.DuplicateSectionError, ValueError): pass for key, value in items.items(): try: self._option_write(config, section, key, value) except ConfigParser.NoSectionError as exp: error_msg = str(exp) error_msg += ( ' Try being more explicit with your override' 'data. Sections are case sensitive.') raise errors.AnsibleModuleError(error_msg) else: config_object.close() resultant_bytesio = io.BytesIO() try: config.write(resultant_bytesio) return resultant_bytesio.getvalue() finally: resultant_bytesio.close()
def check_for_removed_vars(hostvars, host): """Fails if removed variables are found""" found_removed = [] for item in REMOVED_VARIABLES: if item in hostvars[host]: found_removed.append(item) if found_removed: msg = "Found removed variables: " for item in found_removed: msg += "{} is replaced by {}; ".format(item[0], item[1]) raise errors.AnsibleModuleError(msg) return None
def check_strings(strings_to_check): """Check the strings we found to see if they look like file paths and if they are, fail if not start with /etc/origin/master""" for item in strings_to_check: if item.startswith('/') or item.startswith('../'): matches = 0 for allowed in ALLOWED_DIRS: if item.startswith(allowed): matches += 1 if matches == 0: raise errors.AnsibleModuleError( FAIL_MSG.format(ALLOWED_DIRS_STRING, ALLOWED_DIRS_STRING, item, MIGRATED_ITEMS))
def check_image_tag_format(self, hostvars, host, openshift_deployment_type): """Ensure openshift_image_tag is formatted correctly""" openshift_image_tag = self.template_var(hostvars, host, 'openshift_image_tag') if not openshift_image_tag or openshift_image_tag == 'latest': return None regex_to_match = IMAGE_TAG_REGEX[openshift_deployment_type]['re'] res = re.match(regex_to_match, str(openshift_image_tag)) if res is None: msg = IMAGE_TAG_REGEX[openshift_deployment_type]['error_msg'] msg = msg.format(str(openshift_image_tag)) raise errors.AnsibleModuleError(msg)
def check_for_oreg_password(self, hostvars, host, odt): """Ensure oreg_password is defined when using registry.redhat.io""" reg_to_check = 'registry.redhat.io' err_msg = ("oreg_auth_user and oreg_auth_password must be provided when" "deploying openshift-enterprise") err_msg2 = ("oreg_auth_user and oreg_auth_password must be provided when using" "{}".format(reg_to_check)) oreg_password = self.template_var(hostvars, host, 'oreg_auth_password') if oreg_password is not None: # A password is defined, so we're good to go. return None oreg_url = self.template_var(hostvars, host, 'oreg_url') if oreg_url is not None: if reg_to_check in oreg_url: raise errors.AnsibleModuleError(err_msg2) elif odt == 'openshift-enterprise': # We're not using an oreg_url, we're using default enterprise # registry. We require oreg_auth_user and oreg_auth_password raise errors.AnsibleModuleError(err_msg)
def check_python_version(self, hostvars, host, distro): """Ensure python version is 3 for Fedora and python 2 for others""" ansible_python = self.template_var(hostvars, host, 'ansible_python') if distro == "Fedora": if ansible_python['version']['major'] != 3: msg = "openshift-ansible requires Python 3 for {};".format( distro) msg += " For information on enabling Python 3 with Ansible," msg += " see https://docs.ansible.com/ansible/python_3_support.html" raise errors.AnsibleModuleError(msg) else: if ansible_python['version']['major'] != 2: msg = "openshift-ansible requires Python 2 for {};".format( distro)
def build_pv_hostpath(self, varname=None): """Build pv dictionary for hostpath storage type""" volume, size, labels, _, access_modes = self.build_common( varname=varname) # hostpath only supports ReadWriteOnce if access_modes[0] != 'ReadWriteOnce': msg = "Hostpath storage only supports 'ReadWriteOnce' Was given {}." raise errors.AnsibleModuleError(msg.format( access_modes.join(', '))) path = self.get_templated(str(varname) + '_hostpath_path') return dict(name="{0}-volume".format(volume), capacity=size, labels=labels, access_modes=access_modes, storage=dict(hostPath=dict(path=path)))
def network_plugin_check(self, hostvars, host): """Ensure only one type of network plugin is enabled""" res = [] # Loop through each possible network plugin boolean, determine the # actual boolean value, and append results into a list. for plugin, default_val in NET_PLUGIN_LIST: res_temp = self.template_var(hostvars, host, plugin) if res_temp is None: res_temp = default_val res.append(to_bool(res_temp)) if sum(res) not in (0, 1): plugin_str = list(zip([x[0] for x in NET_PLUGIN_LIST], res)) msg = "Host Checked: {} Only one of must be true. Found: {}".format(host, plugin_str) raise errors.AnsibleModuleError(msg)
def no_origin_image_version(self, hostvars, host, openshift_deployment_type): """Ensure we can determine what image version to use with origin fail when: - openshift_is_containerized - openshift_deployment_type == 'origin' - openshift_release is not defined - openshift_image_tag is not defined""" if not openshift_deployment_type == 'origin': return None oic = self.template_var(hostvars, host, 'openshift_is_containerized') if not to_bool(oic): return None orelease = self.template_var(hostvars, host, 'openshift_release') oitag = self.template_var(hostvars, host, 'openshift_image_tag') if not orelease and not oitag: raise errors.AnsibleModuleError(CONTAINERIZED_NO_TAG_ERROR_MSG)
def validate_json_format_vars(self, hostvars, host): """Fails if invalid json format are found""" found_invalid_json = [] for var in JSON_FORMAT_VARIABLES: if var in hostvars[host]: json_var = self.template_var(hostvars, host, var) try: json.loads(json_var) except ValueError as json_err: found_invalid_json.append([var, json_var, json_err]) except BaseException: pass if found_invalid_json: msg = "Found invalid json format variables:\n" for item in found_invalid_json: msg += " {} specified in {} is invalid json format\n {}".format(item[1], item[0], item[2]) raise errors.AnsibleModuleError(msg) return None
def check_unsupported_nfs_configs(self, hostvars, host): """Fails if nfs storage is in use for any components. This check is ignored if openshift_enable_unsupported_configurations=True""" enable_unsupported = self.template_var( hostvars, host, 'openshift_enable_unsupported_configurations') if to_bool(enable_unsupported): return None for storage in STORAGE_KIND_TUPLE: kind = self.template_var(hostvars, host, storage) if kind == 'nfs': raise errors.AnsibleModuleError( 'nfs is an unsupported type for {}. ' 'openshift_enable_unsupported_configurations=True must' 'be specified to continue with this configuration.' ''.format(storage)) return None
def update_nested_conf(conf, update, extend_lists=False): for k, v in update.items(): if isinstance(v, dict): conf[k] = Utils.update_nested_conf( conf.get(k, {}), v, extend_lists) elif k in conf and isinstance(conf[k], list) and extend_lists: if not isinstance(v, list): errmsg = ( "Failure merging key `%(key)s` in dictionary " "`%(dictionary)s`. Expecting a list, but received: " "`%(value)s`, which is of type: `%(type)s`" % { "key": k, "dictionary": conf, "value": v, "type": type(v)} ) raise ansible_errors.AnsibleModuleError(errmsg) conf[k].extend(v) else: conf[k] = v return conf
def check_contains_version(self, hostvars, host): """Fails if variable has old format not containing image version""" found_incorrect = [] for img_var in CHANGED_IMAGE_VARS: img_string = self.template_var(hostvars, host, img_var) if not img_string: return None # split the image string by '/' to account for something like docker:// img_string_parts = img_string.split('/') if ':' not in img_string_parts[-1]: found_incorrect.append((img_var, img_string)) if found_incorrect: msg = ("Found image variables without version. Please ensure any image" "defined contains ':someversion'; ") for item in found_incorrect: msg += "{} was: {}; ".format(item[0], item[1]) raise errors.AnsibleModuleError(msg) return None
def build_pv_dict(self, varname=None): """Check for the existence of PV variables""" kind = self.task_vars.get(str(varname) + '_kind') if kind: kind = self._templar.template(kind) create_pv = self.task_vars.get(str(varname) + '_create_pv') if create_pv and self._templar.template(create_pv): if kind == 'nfs': return self.build_pv_nfs(varname=varname) elif kind == 'openstack': return self.build_pv_openstack(varname=varname) elif kind == 'glusterfs': return self.build_pv_glusterfs(varname=varname) elif not (kind == 'object' or kind == 'dynamic'): msg = "|failed invalid storage kind '{0}' for component '{1}'".format( kind, varname) raise errors.AnsibleModuleError(msg) return None
def build_pv_nfs(self, varname=None): """Build pv dictionary for nfs storage type""" host = self.task_vars.get(str(varname) + '_host') if host: self._templar.template(host) elif host is None: groups = self.task_vars.get('groups') if groups and 'oo_nfs_to_config' in groups and len( groups['oo_nfs_to_config']) > 0: host = groups['oo_nfs_to_config'][0] else: raise errors.AnsibleModuleError( "|failed no storage host detected") volume, size, labels, access_modes = self.build_common(varname=varname) directory = self.get_templated(str(varname) + '_nfs_directory') path = directory + '/' + volume return dict(name="{0}-volume".format(volume), capacity=size, labels=labels, access_modes=access_modes, storage=dict(nfs=dict(server=host, path=path)))
def return_config_overrides_ini(self, config_overrides, resultant, list_extend=True, ignore_none_type=True, default_section='DEFAULT', yml_multilines=False): """Returns string value from a modified config file and dict of merged config :param config_overrides: ``dict`` :param resultant: ``str`` || ``unicode`` :returns: ``str``, ``dict`` """ def _add_section(section_name): # Attempt to add a section to the config file passing if # an error is raised that is related to the section # already existing. try: config.add_section(section_name) except (ConfigParser.DuplicateSectionError, ValueError): pass # If there is an exception loading the RawConfigParser The config obj # is loaded again without the extra option. This is being done to # support older python. try: config = ConfigTemplateParser(allow_no_value=True, dict_type=MultiKeyDict, ignore_none_type=ignore_none_type, default_section=default_section, yml_multilines=yml_multilines, comment_prefixes='/') config.optionxform = str except Exception: config = ConfigTemplateParser(allow_no_value=True, dict_type=MultiKeyDict, comment_prefixes='/') config_object = StringIO(resultant) try: config.read_file(config_object) except AttributeError: config.readfp(config_object) if default_section != 'DEFAULT': _add_section(section_name=default_section) for section, items in config_overrides.items(): # If the items value is not a dictionary it is assumed that the # value is a default item for this config type. if not isinstance(items, dict): if isinstance(items, list): items = ','.join(to_text(i) for i in items) self._option_write(config, default_section, section, items) else: _add_section(section_name=section) for key, value in items.items(): try: self._option_write(config, section, key, value) except ConfigParser.NoSectionError as exp: error_msg = str(exp) error_msg += ( ' Try being more explicit with your override' 'data. Sections are case sensitive.') raise errors.AnsibleModuleError(error_msg) config_object.close() config_dict_new = OrderedDict() config_defaults = config.defaults() for s in config.sections(): config_dict_new[s] = OrderedDict() for k, v in config.items(s): if k not in config_defaults or config_defaults[k] != v: config_dict_new[s][k] = v else: if default_section in config_dict_new: config_dict_new[default_section][k] = v else: config_dict_new[default_section] = {k: v} resultant_stringio = StringIO() try: config.write(resultant_stringio) return resultant_stringio.getvalue(), config_dict_new finally: resultant_stringio.close()
msg: >- One or more incompatibilities were found in {{ config_filename }}. They were {{ compatibility.explanations }} when: - compatibility.incompatible ''' from ansible.module_utils.basic import * from ansible import errors try: import yaml from semver import parse_version_info except ImportError as e: raise errors.AnsibleModuleError(e) REGISTERED_CHECKS = [] # All checks decorated by @register_check will be run by check_compatibility(). def register_check(check): REGISTERED_CHECKS.append(check) return check def get_version(version): '''Return the VersionInfo for the version string, but strip the leading character if it is a v, since that's not strictly semver. ''' if version[0] == 'v':