-
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ee33344
commit 3af5284
Showing
18 changed files
with
340 additions
and
70 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
recursive-include ansible-webui/* | ||
recursive-include ansible-webui/* | ||
include requirements.txt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
from os import environ, getcwd | ||
from pathlib import Path | ||
from secrets import choice as random_choice | ||
from string import digits, ascii_letters, punctuation | ||
from datetime import datetime | ||
|
||
|
||
def get_existing_ansible_config_file() -> str: | ||
# https://docs.ansible.com/ansible/latest/reference_appendices/config.html#the-configuration-file | ||
|
||
for file in [ | ||
getcwd() + '/ansible.cfg', | ||
environ['HOME'] + '/ansible.cfg', | ||
environ['HOME'] + '/.ansible.cfg', | ||
]: | ||
if Path(file).is_file(): | ||
return file | ||
|
||
return '/etc/ansible/ansible.cfg' | ||
|
||
|
||
ENVIRON_FALLBACK = { | ||
'timezone': {'keys': ['AW_TIMEZONE', 'TZ'], 'fallback': datetime.now().astimezone().tzname()}, | ||
'_secret': { | ||
'keys': ['AW_SECRET'], | ||
'fallback': ''.join(random_choice(ascii_letters + digits + punctuation) for _ in range(50)) | ||
}, | ||
'path_base': {'keys': ['AW_PATH_BASE'], 'fallback': '/tmp/ansible-webui'}, | ||
'path_play': {'keys': ['AW_PATH_PLAY', 'ANSIBLE_PLAYBOOK_DIR'], 'fallback': getcwd()}, | ||
'ansible_config': {'keys': ['ANSIBLE_CONFIG'], 'fallback': get_existing_ansible_config_file()} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,20 @@ | ||
# https://docs.ansible.com/ansible/latest/dev_guide/developing_api.html | ||
# https://github.com/ansible/ansible/blob/devel/lib/ansible/cli/playbook.py | ||
# https://github.com/ansible/ansible/blob/devel/lib/ansible/cli/__init__.py | ||
# https://github.com/ansible/ansible/blob/devel/lib/ansible/executor/playbook_executor.py | ||
from datetime import datetime | ||
from ansible_runner import run as ansible_run | ||
|
||
# from ansible.executor.playbook_executor import PlaybookExecutor | ||
from aw.model.job import Job, JobExecution | ||
from aw.execute.util import runner_cleanup, runner_prep, parse_run_result | ||
|
||
from aw.config.main import config | ||
|
||
def ansible_playbook(job: Job, execution: JobExecution): | ||
time_start = datetime.now() | ||
opts = runner_prep(job=job, execution=execution) | ||
|
||
def ansible_playbook(job): | ||
pass | ||
result = ansible_run(**opts) | ||
|
||
parse_run_result( | ||
time_start=time_start, | ||
execution=execution, | ||
result=result, | ||
) | ||
|
||
runner_cleanup(opts) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
from os import chmod | ||
from pathlib import Path | ||
from shutil import rmtree | ||
from random import choice as random_choice | ||
from string import digits | ||
from datetime import datetime | ||
|
||
from ansible_runner import Runner as AnsibleRunner | ||
|
||
from aw.config.main import config | ||
from aw.config.hardcoded import RUNNER_TMP_DIR_TIME_FORMAT | ||
from aw.utils.util import get_choice_key_by_value | ||
from aw.utils.handlers import AnsibleConfigError | ||
from aw.model.job import Job, JobExecution, JobExecutionResult, JobExecutionResultHost, CHOICES_JOB_EXEC_STATUS | ||
|
||
|
||
def _decode_env_vars(env_vars_csv: str, src: str) -> dict: | ||
try: | ||
env_vars = {} | ||
for kv in env_vars_csv.split(','): | ||
k, v = kv.split('=') | ||
env_vars[k] = v | ||
|
||
return env_vars | ||
|
||
except ValueError: | ||
raise AnsibleConfigError( | ||
f"Environmental variables of {src} are not in a valid format " | ||
f"(comma-separated key-value pairs). Example: 'key1=val1,key2=val2'" | ||
) | ||
|
||
|
||
def _update_execution_status(execution: JobExecution, status: str): | ||
execution.status = get_choice_key_by_value(choices=CHOICES_JOB_EXEC_STATUS, value=status) | ||
execution.save() | ||
|
||
|
||
def _runner_options(job: Job, execution: JobExecution) -> dict: | ||
# NOTES: | ||
# playbook str or list | ||
# project_dir = playbook_dir | ||
# quiet | ||
# limit, verbosity, envvars | ||
|
||
# build unique temporary execution directory | ||
path_base = config['path_base'] | ||
if not path_base.endswith('/'): | ||
path_base += '/' | ||
|
||
path_base += datetime.now().strftime(RUNNER_TMP_DIR_TIME_FORMAT) | ||
path_base += ''.join(random_choice(digits) for _ in range(5)) | ||
|
||
# merge job + execution env-vars | ||
env_vars = {} | ||
if job.environment_vars is not None: | ||
env_vars = { | ||
**env_vars, | ||
**_decode_env_vars(env_vars_csv=job.environment_vars, src='Job') | ||
} | ||
|
||
if execution.environment_vars is not None: | ||
env_vars = { | ||
**env_vars, | ||
**_decode_env_vars(env_vars_csv=execution.environment_vars, src='Execution') | ||
} | ||
|
||
return { | ||
'runner_mode': 'pexpect', | ||
'private_data_dir': path_base, | ||
'project_dir': config['path_play'], | ||
'quiet': True, | ||
'limit': execution.limit if execution.limit is not None else job.limit, | ||
'envvars': env_vars, | ||
} | ||
|
||
|
||
def runner_prep(job: Job, execution: JobExecution): | ||
_update_execution_status(execution, status='Starting') | ||
|
||
opts = _runner_options(job=job, execution=execution) | ||
opts['playbook'] = job.playbook.split(',') | ||
opts['inventory'] = job.inventory.split(',') | ||
|
||
# https://docs.ansible.com/ansible/2.8/user_guide/playbooks_best_practices.html#directory-layout | ||
project_dir = opts['project_dir'] | ||
if not project_dir.endswith('/'): | ||
project_dir += '/' | ||
|
||
for playbook in opts['playbook']: | ||
ppf = project_dir + playbook | ||
if not Path(ppf).is_file(): | ||
raise AnsibleConfigError(f"Configured playbook not found: '{ppf}'") | ||
|
||
for inventory in opts['inventory']: | ||
pi = project_dir + inventory | ||
if not Path(pi).exists(): | ||
raise AnsibleConfigError(f"Configured inventory not found: '{pi}'") | ||
|
||
pdd = Path(opts['private_data_dir']) | ||
if not pdd.is_dir(): | ||
pdd.mkdir() | ||
chmod(path=pdd, mode=0o750) | ||
|
||
_update_execution_status(execution, status='Running') | ||
|
||
|
||
def runner_cleanup(opts: dict): | ||
rmtree(opts['private_data_dir']) | ||
|
||
|
||
def parse_run_result(execution: JobExecution, time_start: datetime, result: AnsibleRunner): | ||
job_result = JobExecutionResult( | ||
time_start=time_start, | ||
failed=result.errored, | ||
) | ||
job_result.save() | ||
|
||
# https://stackoverflow.com/questions/70348314/get-python-ansible-runner-module-stdout-key-value | ||
for host in result.stats['processed']: | ||
result_host = JobExecutionResultHost() | ||
|
||
result_host.unreachable = host in result.stats['unreachable'] | ||
result_host.tasks_skipped = result.stats['skipped'][host] if host in result.stats['skipped'] else 0 | ||
result_host.tasks_ok = result.stats['ok'][host] if host in result.stats['ok'] else 0 | ||
result_host.tasks_failed = result.stats['failures'][host] if host in result.stats['failures'] else 0 | ||
result_host.tasks_ignored = result.stats['ignored'][host] if host in result.stats['ignored'] else 0 | ||
result_host.tasks_rescued = result.stats['rescued'][host] if host in result.stats['rescued'] else 0 | ||
result_host.tasks_changed = result.stats['changed'][host] if host in result.stats['changed'] else 0 | ||
|
||
if result_host.tasks_failed > 0: | ||
# todo: create errors | ||
pass | ||
|
||
result_host.result = job_result | ||
result_host.save() | ||
|
||
execution.result = job_result | ||
if job_result.failed: | ||
_update_execution_status(execution, status='Failed') | ||
|
||
else: | ||
_update_execution_status(execution, status='Finished') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
from django.db import models | ||
|
||
BOOLEAN_CHOICES = ( | ||
CHOICES_BOOL = ( | ||
(True, 'Yes'), | ||
(False, 'No') | ||
) | ||
|
Oops, something went wrong.