Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#63 improved logging of ansible tasks #64

Merged
merged 7 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/check_ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ jobs:

- name: Run pytest
run: >
poetry run pytest
-o log_cli=true
poetry run pytest -o log_cli=true -o log_cli_level=INFO
test/unit
test/integration/test_create_dss_docker_image.py
env: # Set the secret as an env variable
Expand Down
2 changes: 2 additions & 0 deletions doc/changes/changes_0.1.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ Version: 0.1.0
## Bug Fixes

* #1: Fixed CI build
* #61: Change initial password of Jupyter notebooks to "dss"

## Refactoring

* #5: Renamed all occurrences of "script language developer" by "data science"
* #56: Moved jupyter notebook files again
* #63: Improved logging of Ansible tasks

## Documentation

Expand Down
27 changes: 20 additions & 7 deletions exasol/ds/sandbox/lib/ansible/ansible_access.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import ansible_runner
ckunki marked this conversation as resolved.
Show resolved Hide resolved
import json
import logging

from typing import Callable
from dataclasses import dataclass
from typing import Callable, Dict, Optional

from exasol.ds.sandbox.lib.ansible.ansible_run_context import AnsibleRunContext
from exasol.ds.sandbox.lib.logging import get_status_logger, LogType
Expand All @@ -11,19 +13,30 @@ class AnsibleException(RuntimeError):
pass


AnsibleEvent = Dict[str, any]


class AnsibleAccess:
"""
Provides access to ansible runner.
@raises: AnsibleException if ansible execution fails
"""
@staticmethod
def run(private_data_dir: str, run_ctx: AnsibleRunContext, printer: Callable[[str], None]):
def run(
private_data_dir: str,
run_ctx: AnsibleRunContext,
event_logger: Callable[[str], None],
event_handler: Callable[[AnsibleEvent], bool] = None,
):
quiet = not get_status_logger(LogType.ANSIBLE).isEnabledFor(logging.INFO)
r = ansible_runner.run(private_data_dir=private_data_dir,
playbook=run_ctx.playbook,
quiet=quiet,
extravars=run_ctx.extra_vars)
r = ansible_runner.run(
private_data_dir=private_data_dir,
playbook=run_ctx.playbook,
quiet=quiet,
event_handler=event_handler,
extravars=run_ctx.extra_vars,
)
for e in r.events:
printer(e)
event_logger(json.dumps(e, indent=2))
if r.rc != 0:
raise AnsibleException(r.rc)
35 changes: 30 additions & 5 deletions exasol/ds/sandbox/lib/ansible/ansible_runner.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import logging

from pathlib import Path
from typing import Tuple

from exasol.ds.sandbox.lib.ansible.ansible_access import AnsibleAccess
from exasol.ds.sandbox.lib.ansible.ansible_access import AnsibleAccess, AnsibleEvent
from exasol.ds.sandbox.lib.ansible.ansible_run_context import AnsibleRunContext
from exasol.ds.sandbox.lib.logging import get_status_logger, LogType
from exasol.ds.sandbox.lib.setup_ec2.host_info import HostInfo
Expand All @@ -17,13 +19,36 @@ class AnsibleRunner:
def __init__(self, ansible_access: AnsibleAccess, work_dir: Path):
self._ansible_access = ansible_access
self._work_dir = work_dir
self._duration_logger = AnsibleRunner.duration_logger()

@classmethod
def duration_logger(cls) -> logging.Logger:
def handler():
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter('%(message)s'))
return handler
logger = logging.getLogger(f"{__name__}:{cls.__name__}")
logger.setLevel(logging.DEBUG)
logger.propagate = False
logger.addHandler(handler())
return logger

@staticmethod
def printer(msg: str):
LOG.debug(msg)
def event_handler(self, event: AnsibleEvent) -> bool:
if not "event_data" in event:
return True
duration = event["event_data"].get("duration", 0)
if duration > 0.5:
self._duration_logger.debug(f"duration: {round(duration)} seconds")
return True

def run(self, ansible_run_context: AnsibleRunContext, host_infos: Tuple[HostInfo]):
inventory_content = render_template("inventory.jinja", host_infos=host_infos)
with open(self._work_dir / "inventory", "w") as f:
f.write(inventory_content)
self._ansible_access.run(str(self._work_dir), ansible_run_context, self.printer)
event_handler = self.event_handler if LOG.isEnabledFor(logging.INFO) else None
ckunki marked this conversation as resolved.
Show resolved Hide resolved
self._ansible_access.run(
str(self._work_dir),
ansible_run_context,
event_logger=LOG.debug,
event_handler=self.event_handler,
)
3 changes: 3 additions & 0 deletions exasol/ds/sandbox/runtime/ansible/general_setup_tasks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
upgrade: yes
update_cache: yes
become: "{{need_sudo}}"
- name: Install rsync
include_role:
name: rsync
ckunki marked this conversation as resolved.
Show resolved Hide resolved
- name: Install Poetry
include_role:
name: poetry
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ jupyterlab:
ip: '*'
config: "{{user_home}}/.jupyter/jupyter_lab_config.py"
virtualenv: "{{user_home}}/jupyterenv"
password: "{{ lookup('ansible.builtin.env', 'JUPYTER_LAB_PASSWORD', default='script-languages') }}"
password: "{{ lookup('ansible.builtin.env', 'JUPYTER_LAB_PASSWORD', default='dss') }}"
notebook_folder: "{{user_home}}/notebooks"

apt_dependencies:
- rsync=3.1.3-8ubuntu0.7
- virtualenv=20.0.17-1ubuntu0.4
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
- name: Copy notebook content
ansible.builtin.copy:
ansible.builtin.synchronize:
src: "notebook/"
dest: "{{jupyterlab.notebook_folder}}"
mode: 0644
rsync_opts:
- "--chmod=0644"
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---

apt_dependencies:
- rsync=3.1.3-8ubuntu0.7
6 changes: 6 additions & 0 deletions exasol/ds/sandbox/runtime/ansible/roles/rsync/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- name: Install rsync
apt:
name: "{{apt_dependencies}}"
state: latest
update_cache: true
become: "{{need_sudo}}"
2 changes: 2 additions & 0 deletions test/integration/test_create_dss_docker_image.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import docker
import io
import pytest
import re
import requests
import sys
import tenacity
import time
import typing
Expand Down
9 changes: 7 additions & 2 deletions test/unit/test_ansible.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
AnsibleResourceRepository
from exasol.ds.sandbox.lib.ansible.ansible_run_context import AnsibleRunContext, \
default_ansible_run_context
from exasol.ds.sandbox.lib.ansible.ansible_access import AnsibleEvent
from exasol.ds.sandbox.lib.setup_ec2.host_info import HostInfo
from exasol.ds.sandbox.lib.setup_ec2.run_install_dependencies import run_install_dependencies

Expand All @@ -22,8 +23,12 @@ def __init__(self, delegate: Optional[Callable[[str, AnsibleRunContext], None]]
self.arguments = namedtuple("Arguments", "private_data_dir run_ctx")
self.delegate = delegate

def run(self, private_data_dir: str, run_ctx: AnsibleRunContext, printer: Callable[[str], None]):

def run(self,
private_data_dir: str,
run_ctx: AnsibleRunContext,
event_handler: Callable[[AnsibleEvent], bool],
event_logger: Callable[[str], None],
):
self.call_arguments = self.arguments(private_data_dir, run_ctx)
if self.delegate is not None:
self.delegate(private_data_dir, run_ctx)
Expand Down
Loading