ublue-forge/anvil/nicegui/pages/ansible.py
2024-05-10 16:03:05 +02:00

122 lines
4.5 KiB
Python

import ansible_runner
import re
import asyncio
import os
from nicegui import ui
from theme import GuiProgressSpinner
from utils import local_file_picker
ANSIBLE_EXTRA_VARS = None
# Ansible integration
@ui.refreshable # https://www.reddit.com/r/nicegui/comments/1bphjk5/comment/kx7l5kj/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button
async def load_configuration_file() -> None:
global ANSIBLE_EXTRA_VARS
result = await local_file_picker(
directory="/data", multiple=False, file_name_filter=".yml"
)
file_path = result[0]
with open(file_path, "r") as file:
data = file.read()
## Give feedback to user
ui.notify(f"You chose {file_path}")
## Display content
ui.code(content=data, language="yaml")
## Preserve configuration file path for ansible-playbook --extra-vars
ANSIBLE_EXTRA_VARS = f"@{file_path}"
async def run_ansible_playbook(
playbook_name: str, gui_log: ui.log, gui_spinner: GuiProgressSpinner
) -> None:
# Clear log console
gui_log.clear()
# Enable spinner
gui_spinner.enable()
# Run ansible playbook
project_root = os.environ["ANSIBLE_DIR"]
playbook_path = f"{project_root}/playbooks/"
extra_vars_file = ANSIBLE_EXTRA_VARS
thread, runner = ansible_runner.interface.run_command_async(
executable_cmd="ansible-playbook",
cmdline_args=[
f"{playbook_path}/{playbook_name}",
# playbook_path + playbook_name,
"--extra-vars",
extra_vars_file,
],
)
# Parse and display output from ansible playbook
## Remove color characters from response until clear how to display them in a log
output_parser = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-9;#]+[mGK]?)")
# show log from asynchronous job
processed_events = set() # Set to store processed events
while runner.rc is None:
for event in runner.events:
# Make sure log is displayed during playbook run
await asyncio.sleep(0.1)
# Check if event has been processed already
event_key = (event["uuid"], event["counter"])
if event_key not in processed_events:
# Add event to processed set
processed_events.add(event_key)
# Process event
ansible_log = format(output_parser.sub("", event["stdout"]))
# Push log to ui
gui_log.push(ansible_log)
# Disable spinner
gui_spinner.disable()
# Page content
def content() -> None:
with ui.row().classes("w-full"):
with ui.row().classes("w-full"):
with ui.card().classes("h-full"):
ui.button(
text="Load configuration file",
on_click=lambda: load_configuration_file.refresh(),
icon="folder",
)
## show configuration file with ui.code
load_configuration_file()
with ui.row().classes("w-full"):
# First Row
with ui.card().classes("h-full"):
with ui.row().classes("no-wrap"):
ui.label("Build").classes("text-h5")
gui_build_progress = GuiProgressSpinner()
ui.button(
text="Clone project",
on_click=lambda: run_ansible_playbook(
playbook_name="project_clone.yml",
gui_log=gui_playbook_log,
gui_spinner=gui_build_progress,
),
)
ui.button(
text="Build project",
on_click=lambda: run_ansible_playbook(
"project_build.yml",
gui_log=gui_playbook_log,
gui_spinner=gui_build_progress,
),
)
# Second Row
with ui.card().classes("h-full"):
with ui.row().classes("no-wrap"):
ui.label("Deploy").classes("text-h6")
gui_deploy_progress = GuiProgressSpinner
ui.button(
"Deploy VM",
on_click=lambda: ui.notify("This playbook is not implemented yet"),
)
with ui.row().classes("w-full"):
with ui.card().classes("w-full"):
ui.label("Playbook Log").classes("text-h6")
ui.button("Clear Log", on_click=lambda: gui_playbook_log.clear())
gui_playbook_log = ui.log().classes("w-full h-full")