diff --git a/plugins/action/get_all_site.py b/plugins/action/get_all_site.py new file mode 100644 index 0000000..317f48d --- /dev/null +++ b/plugins/action/get_all_site.py @@ -0,0 +1,211 @@ +# Copyright: (c) 2016, Allen Sanabria +# 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 + +from os import path, walk +import re + +import ansible.constants as C +from ansible.errors import AnsibleError +from ansible.module_utils.six import string_types +from ansible.module_utils.common.text.converters import to_native, to_text +from ansible.plugins.action import ActionBase +from ansible.template import Templar +from ansible.utils.vars import combine_vars +from pathlib import Path + + +class ActionModule(ActionBase): + + TRANSFERS_FILES = False + _requires_connection = False + + def run(self, tmp=None, task_vars=None): + """ Load yml files recursively from a directory. + """ + del tmp # tmp no longer has any effect + + if task_vars is None: + task_vars = dict() + self.task_var = task_vars + self.show_content = True + self.included_files = [] + + # Validate arguments + dirs = 0 + + module_args = self._task.args.copy() + + if not "dir" in module_args: + raise AnsibleError('\'dir\' option is mendatory in load_hasites_config') + if not "default_domain" in module_args: + raise AnsibleError('\'default_domain\' option is mendatory in load_hasites_config') + + self.source_dir = module_args.get('dir') + self.default_domain = module_args.get('default_domain') + self.dir = module_args.get('dir') + self.depth = module_args.get('depth', 0) + + results = { + "sites": [], + + } + failed = False + + self._set_root_dir() + if not path.exists(self.source_dir): + failed = True + err_msg = ('{0} directory does not exist'.format(to_native(self.source_dir))) + elif not path.isdir(self.source_dir): + failed = True + err_msg = ('{0} is not a directory'.format(to_native(self.source_dir))) + else: + for root_dir, filenames in self._traverse_dir_depth(): + failed, err_msg, updated_results = (self._load_files_in_dir(root_dir, filenames)) + if failed: + break + results['sites'] = results['sites'] + updated_results['sites'] + + + result = super(ActionModule, self).run(task_vars=task_vars) + + if failed: + result['failed'] = failed + result['message'] = err_msg + scope = dict() + scope['caddy_config'] = results + results = scope + result['ansible_included_var_files'] = self.included_files + if not "target_var" in module_args: + result['ansible_facts'] = results + else: + result['ansible_facts'][module_args['target_var']] = results + result['_ansible_no_log'] = not self.show_content + + return result + + def _set_root_dir(self): + if self._task._role: + if self.source_dir.split('/')[0] == 'vars': + path_to_use = ( + path.join(self._task._role._role_path, self.source_dir) + ) + if path.exists(path_to_use): + self.source_dir = path_to_use + else: + path_to_use = ( + path.join( + self._task._role._role_path, 'vars', self.source_dir + ) + ) + self.source_dir = path_to_use + else: + if hasattr(self._task._ds, '_data_source'): + current_dir = ( + "/".join(self._task._ds._data_source.split('/')[:-1]) + ) + self.source_dir = path.join(current_dir, self.source_dir) + + def _log_walk(self, error): + self._display.vvv('Issue with walking through "%s": %s' % (to_native(error.filename), to_native(error))) + + def _traverse_dir_depth(self): + """ Recursively iterate over a directory and sort the files in + alphabetical order. Do not iterate pass the set depth. + The default depth is unlimited. + """ + current_depth = 0 + sorted_walk = list(walk(self.source_dir, onerror=self._log_walk, followlinks=True)) + sorted_walk.sort(key=lambda x: x[0]) + for current_root, current_dir, current_files in sorted_walk: + current_depth += 1 + if current_depth <= self.depth or self.depth == 0: + current_files.sort() + yield (current_root, current_files) + else: + break + + def _load_files(self, filename): + """ Loads a file and converts the output into a valid Python dict. + Args: + filename (str): The source file. + + Returns: + Tuple (bool, str, dict) + """ + results = dict() + failed = False + err_msg = '' + b_data, show_content = self._loader._get_file_contents(filename) + data = to_text(b_data, errors='surrogate_or_strict') + + self.show_content = show_content + data = self._loader.load(data, file_name=filename, show_content=show_content) + if not data: + data = dict() + if not isinstance(data, dict): + failed = True + err_msg = ('{0} must be stored as a dictionary/hash'.format(to_native(filename))) + else: + # Apply Ansible templating to the data + templar = Templar(loader=self._loader, variables=self.task_var) + data = templar.template(data) + + self.included_files.append(filename) + results.update(data) + + return failed, err_msg, results + + def _load_files_in_dir(self, root_dir, var_files): + """ Load the found yml files and update/overwrite the dictionary. + Args: + root_dir (str): The base directory of the list of files that is being passed. + var_files: (list): List of files to iterate over and load into a dictionary. + + Returns: + Tuple (bool, str, dict) + """ + results = { + "sites": [] + } + failed = False + err_msg = '' + for filename in var_files: + stop_iter = False + # Never include main.yml from a role, as that is the default included by the role + if self._task._role: + if path.join(self._task._role._role_path, filename) == path.join(root_dir, 'vars', 'main.yml'): + stop_iter = True + continue + + filepath = path.join(root_dir, filename) + if not stop_iter and not failed: + if path.exists(filepath): + loaded_data = {} + failed, err_msg, loaded_data = self._load_files(filepath) + if not failed: + main_hostname = Path(filepath).stem + + domain = dns.get("domain", self.default_domain) + additionnal_hostname = loaded_data.get('additionnal_hostname', []) + state = loaded_data.get("state", "present") + + if "upstream" not in loaded_data: + failed = True + err_msg = ('Could not find "upstream" in {0}'.format(to_native(filename))) + continue + if state == "present": + results['sites'].append('{0}.{1}'.format(main_hostname, domain)) + + for host in additionnal_hostname: + this_domain = this_dns.get("domain", domain) + this_state = host.get('state', state) + full_domain = '{0}.{1}'.format(host.get("hostname"), this_domain) if host.get("hostname") else this_domain + + this_upstream_config = host.get("upstream_config") + + if this_state == "present": + results['sites'].append(full_domain) + return failed, err_msg, results \ No newline at end of file