#!/usr/bin/python # Copyright: (c) 2018, Terry Jones # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import (absolute_import, division, print_function) __metaclass__ = type DOCUMENTATION = r''' --- module: CreoleSet short_description: This module aims to set variables on EOLE modules version_added: "1.0.0" description: This module aims to set variables on EOLE modules orderly and atomicly. options: variables: description: Name of the variable to change. required: true type: list state: description: state of variables required: false type: str author: - Cadoles ''' EXAMPLES = r''' variables: - name: activer_ajout_hosts value: 'oui' - name: adresse_ip_hosts value: - 192.168.1.1 - name: nom_long_hosts value: - "seth.domain.org cloud.domain.org" state: exact ''' RETURN = r''' # These are examples of possible return values, and in general should use other names for return values. diff: description: variable modifications status. type: list returned: always sample: ['activer_ajout_hosts: non => oui'] ''' import sys from ansible.module_utils.basic import AnsibleModule from creole.loader import creole_loader, config_save_values def process_value(variable, value): if variable.impl_is_multi(): if not isinstance(value, list): return [value] else: if isinstance(value, list): raise Exception('Variable {} is not multi'.format(variable)) return value def yml_params_to_unicode(param): def convert_param(param): if isinstance(param, str): return param.decode('utf-8') if isinstance(param, list): return [convert_param(p) for p in param] if isinstance(param, dict): return {convert_param(key): convert_param(value) for key,value in param.items()} return param return convert_param(param) def run_module(): # define available arguments/parameters a user can pass to the module module_args = dict( variables=dict(type='list', required=True), state=dict(type='str', required=False, default='exact'), ) # seed the result dict in the object # we primarily care about changed and state # changed is if this module effectively modified the target # state will include any data that you want your module to pass back # for consumption, for example, in a subsequent task result = dict( changed=False, diff=[], debug=[], ) # the AnsibleModule object will be our abstraction working with Ansible # this includes instantiation, a couple of common attr would be the # args/params passed to the execution, as well as if the module # supports check mode module = AnsibleModule( argument_spec=module_args, supports_check_mode=True ) # if the user is working with this module in only check mode we do not # want to make any changes to the environment, just return the current # state with no modifications c = creole_loader(rw=True, reload_config=True) d = c.cfgimpl_get_description() diff = [] c.cfgimpl_get_settings().remove('disabled') if sys.version_info < (3,): module.params['variables'] = yml_params_to_unicode(module.params['variables']) variables = {} for variable in module.params['variables']: creole_variable = c.find_first(byname=variable['name']) variables[creole_variable] = process_value(creole_variable, variable['value']) slaves = [v for v in variables if v.impl_is_master_slaves('slave')] masters = {v: [] for v in variables if v.impl_is_master_slaves('master')} for slave in slaves: master = slave.impl_get_master_slaves().getmaster(slave) masters[master].append(slave) ordered_variables = [(v, []) for v in variables if not v.impl_is_master_slaves()] ordered_variables.extend(masters.items()) for variable, sub_variables in ordered_variables: new_value = variables[variable] var_path = d.impl_get_path_by_opt(variable) old_value = c.getattr(var_path) new_value_set = {var_path: {'value': new_value, 'sub': [(d.impl_get_path_by_opt(sub), variables[sub]) for sub in sub_variables]}} if sys.version_info < (3,): new_value_set = yml_params_to_unicode(new_value_set) if variable.impl_is_multi(): method = module.params['state'] if method == 'exact': if old_value != new_value: diff.append('{}: {} => {}'.format(var_path, old_value, new_value)) if sub_variables: sub_old_values = [] for sub_variable in sub_variables: sub_value = variables[sub_variable] sub_var_path = d.impl_get_path_by_opt(sub_variable) sub_old_value = c.getattr(sub_var_path) sub_old_values.append(sub_old_value) if sub_old_value != sub_value: diff.append('{}: {} => {}'.format(sub_var_path, sub_old_value, sub_value)) homeconfig, name = c.cfgimpl_get_home_by_path(var_path) homeconfig.__delattr__(name) c.cfgimpl_get_settings().remove('validator') c.setattr(var_path, new_value) for sub_variable, sub_old_value in zip(sub_variables, sub_old_values): sub_value = variables[sub_variable] sub_var_path = d.impl_get_path_by_opt(sub_variable) if sub_old_value != sub_value: for i in range(len(new_value)): c.getattr(sub_var_path)[i] = sub_value[i] c.cfgimpl_get_settings().append('validator') else: c.setattr(var_path, new_value) elif method == 'present': # is master value present in old values? indexes = {} for nv in new_value: indexes[nv] = [] for index, ov in enumerate(old_value): if nv == ov: indexes[nv].append(index) for nv in indexes: nv_index = new_value_set[var_path]['value'].index(nv) new_value_subset = {so: sv[nv_index] for so,sv in new_value_set[var_path]['sub']} is_present = False for index in indexes[nv]: old_value_set = {d.impl_get_path_by_opt(sv): c.getattr(d.impl_get_path_by_opt(sv))[index] for sv in sub_variables} if old_value_set == new_value_subset: is_present = True break if not is_present: old_value.append(nv) diff.append('{}: {} => {}'.format(var_path, old_value[:-1], old_value)) for sub_variable, sub_value in new_value_set[var_path]['sub']: c.getattr(sub_variable)[-1] = sub_value[nv_index] diff.append('{}: {} => {}'.format(sub_variable, c.getattr(sub_variable)[:-1], c.getattr(sub_variable))) else: if old_value != new_value: diff.append('{}: {} => {}'.format(var_path, old_value, new_value)) c.setattr(var_path, new_value) for sub_variable in sub_variables: sub_value = variables[sub_variable] sub_var_path = d.impl_get_path_by_opt(sub_variable) sub_old_value = c.getattr(sub_var_path) if sub_old_value != sub_value: diff.append('{}: {} => {}'.format(sub_var_path, sub_old_value, sub_value)) c.setattr(sub_var_path, sub_value) result['diff'] = diff c.cfgimpl_get_settings().append('disabled') c.make_dict() if not module.check_mode and diff: config_save_values(c, 'creole') result['changed'] = True module.exit_json(**result) def main(): run_module() if __name__ == '__main__': main()