From bb9a6340d6e3c09aaaf8111148bf123244ff341c Mon Sep 17 00:00:00 2001 From: timeforplanb123 Date: Sun, 3 Apr 2022 22:51:13 +0300 Subject: [PATCH] Add new functions - print_stat - Prints results statistic on stdout. - write_result - Writes results to file. - write_results - Writes results to files. --- README.rst | 3 + .../tutorials/function_print_stat.ipynb | 220 ++++ .../tutorials/function_write_result.ipynb | 871 ++++++++++++++++ .../tutorials/function_write_results.ipynb | 959 ++++++++++++++++++ nornir_utils/plugins/functions/__init__.py | 17 +- nornir_utils/plugins/functions/print_stat.py | 96 ++ .../plugins/functions/write_result.py | 182 ++++ .../plugins/functions/write_results.py | 203 ++++ 8 files changed, 2549 insertions(+), 2 deletions(-) create mode 100644 docs/source/tutorials/function_print_stat.ipynb create mode 100644 docs/source/tutorials/function_write_result.ipynb create mode 100644 docs/source/tutorials/function_write_results.ipynb create mode 100644 nornir_utils/plugins/functions/print_stat.py create mode 100644 nornir_utils/plugins/functions/write_result.py create mode 100644 nornir_utils/plugins/functions/write_results.py diff --git a/README.rst b/README.rst index f5096ce..cecf961 100644 --- a/README.rst +++ b/README.rst @@ -31,6 +31,9 @@ _________ * **print_result** - Formats nicely and prints results on stdout. * **print_title** - Formats nicely a title and prints it on stdout. +* **print_stat** - Prints results statistic on stdout. +* **write_result** - Writes results to file. +* **write_results** - Writes results to files. Processors __________ diff --git a/docs/source/tutorials/function_print_stat.ipynb b/docs/source/tutorials/function_print_stat.ipynb new file mode 100644 index 0000000..b80a381 --- /dev/null +++ b/docs/source/tutorials/function_print_stat.ipynb @@ -0,0 +1,220 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "57c50027", + "metadata": {}, + "source": [ + "# functions: print_stat.py\n", + "\n", + "[print_stat](../api/functions.rst#nornir_utils.plugins.functions.print_stat) is a function that prints result statistic:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "846f5194", + "metadata": {}, + "outputs": [], + "source": [ + "from nornir import InitNornir\n", + "\n", + "def dummy_task(task):\n", + " return \"hi!!!\"\n", + "\n", + "nr = InitNornir(\n", + " inventory={\n", + " \"plugin\":\"YAMLInventory\",\n", + " \"options\": {\n", + " \"host_file\": \"data/hosts.yaml\",\n", + " \"group_file\": \"data/groups.yaml\",\n", + " \"defaults_file\": \"data/defaults.yaml\",\n", + " }\n", + " }\n", + ")\n", + "\n", + "result = nr.run(task=dummy_task)" + ] + }, + { + "cell_type": "markdown", + "id": "eaf219ba", + "metadata": {}, + "source": [ + "Now we could print the result statistic easily with the `print_stat` function: " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ab8f58ca", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[36mdummy_task**********************************************************************\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[34m* dev1.group_1 *****************************************************************\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[32mdummy_task ok=1 changed=0 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[34m* dev2.group_1 *****************************************************************\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[32mdummy_task ok=1 changed=0 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[34m* dev3.group_2 *****************************************************************\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[32mdummy_task ok=1 changed=0 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[34m* dev4.group_2 *****************************************************************\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[32mdummy_task ok=1 changed=0 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[34m* dev5.no_group ****************************************************************\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[32mdummy_task ok=1 changed=0 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[32mOK : 5\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[33mCHANGED : 0\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[31mFAILED : 0\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "from nornir_utils.plugins.functions import print_stat\n", + "\n", + "print_stat(result)" + ] + }, + { + "cell_type": "markdown", + "id": "03d245e5", + "metadata": {}, + "source": [ + "If the task returns changed or failed results, you can see it:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "d6fa941a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[36mdummy_task**********************************************************************\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[34m* dev1.group_1 *****************************************************************\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[33mdummy_task ok=1 changed=1 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[34m* dev2.group_1 *****************************************************************\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[31mdummy_task ok=0 changed=0 failed=1 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[34m* dev3.group_2 *****************************************************************\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[32mdummy_task ok=1 changed=0 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[34m* dev4.group_2 *****************************************************************\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[31mdummy_task ok=0 changed=0 failed=1 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[34m* dev5.no_group ****************************************************************\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[32mdummy_task ok=1 changed=0 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[32mOK : 3\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[33mCHANGED : 1\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[31mFAILED : 2\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "result[\"dev1.group_1\"][0].changed=True\n", + "result[\"dev2.group_1\"][0].failed=True\n", + "result[\"dev4.group_2\"][0].failed=True\n", + "\n", + "print_stat(result)" + ] + }, + { + "cell_type": "markdown", + "id": "948feecb", + "metadata": {}, + "source": [ + "If `MultiResult` object contains multiple `Result` objects:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "0d811189", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[36msmart_task**********************************************************************\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[34m* dev1.group_1 *****************************************************************\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[31msmart_task ok=0 changed=0 failed=1 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[32mdummy_task ok=1 changed=0 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[32mdummy_task ok=1 changed=0 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[32mdummy_task ok=1 changed=0 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[31mdummy_task ok=0 changed=0 failed=1 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[34m* dev2.group_1 *****************************************************************\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[31msmart_task ok=0 changed=0 failed=1 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[32mdummy_task ok=1 changed=0 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[32mdummy_task ok=1 changed=0 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[32mdummy_task ok=1 changed=0 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[31mdummy_task ok=0 changed=0 failed=1 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[34m* dev3.group_2 *****************************************************************\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[31msmart_task ok=0 changed=0 failed=1 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[32mdummy_task ok=1 changed=0 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[32mdummy_task ok=1 changed=0 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[32mdummy_task ok=1 changed=0 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[31mdummy_task ok=0 changed=0 failed=1 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[34m* dev4.group_2 *****************************************************************\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[31msmart_task ok=0 changed=0 failed=1 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[32mdummy_task ok=1 changed=0 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[32mdummy_task ok=1 changed=0 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[32mdummy_task ok=1 changed=0 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[31mdummy_task ok=0 changed=0 failed=1 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[34m* dev5.no_group ****************************************************************\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[31msmart_task ok=0 changed=0 failed=1 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[32mdummy_task ok=1 changed=0 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[32mdummy_task ok=1 changed=0 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[32mdummy_task ok=1 changed=0 failed=0 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m \u001b[1m\u001b[31mdummy_task ok=0 changed=0 failed=1 \u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[32mOK : 15\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[33mCHANGED : 0\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[31mFAILED : 10\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "def smart_task(task):\n", + " task.run(task=dummy_task)\n", + " task.run(task=dummy_task)\n", + " task.run(task=dummy_task)\n", + " task.run(task=dummy_task, failed=True)\n", + " return \"hihi!!!\"\n", + "\n", + "\n", + "smart_task_result = nr.run(task=smart_task)\n", + "\n", + "print_stat(smart_task_result)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/source/tutorials/function_write_result.ipynb b/docs/source/tutorials/function_write_result.ipynb new file mode 100644 index 0000000..45b1c5a --- /dev/null +++ b/docs/source/tutorials/function_write_result.ipynb @@ -0,0 +1,871 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "2d2c17c5", + "metadata": {}, + "source": [ + "# functions: write_result\n", + "\n", + "[write_result](../api/functions.rst#nornir_utils.plugins.functions.write_result) is a function that writes result and returns diff between old file and new file:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "03a00337", + "metadata": {}, + "outputs": [], + "source": [ + "from nornir import InitNornir\n", + "\n", + "def dummy_task(task):\n", + " return \"hi!!!\"\n", + "\n", + "nr = InitNornir(\n", + " inventory={\n", + " \"plugin\":\"YAMLInventory\",\n", + " \"options\": {\n", + " \"host_file\": \"data/hosts.yaml\",\n", + " \"group_file\": \"data/groups.yaml\",\n", + " \"defaults_file\": \"data/defaults.yaml\",\n", + " }\n", + " }\n", + ")\n", + "\n", + "result = nr.run(task=dummy_task)" + ] + }, + { + "cell_type": "markdown", + "id": "57e43930", + "metadata": {}, + "source": [ + "Now we could write the result easily with the `write_result` function:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "cb99b478", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'--- out_files/write_result/result.txt\\n\\n+++ new\\n\\n@@ -0,0 +1,19 @@\\n\\n+---- dev1.group_1: dummy_task ** changed : False ------------------------------- INFO\\n+\\n+hi!!!\\n+\\n+---- dev2.group_1: dummy_task ** changed : False ------------------------------- INFO\\n+\\n+hi!!!\\n+\\n+---- dev3.group_2: dummy_task ** changed : False ------------------------------- INFO\\n+\\n+hi!!!\\n+\\n+---- dev4.group_2: dummy_task ** changed : False ------------------------------- INFO\\n+\\n+hi!!!\\n+\\n+---- dev5.no_group: dummy_task ** changed : False ------------------------------ INFO\\n+\\n+hi!!!'" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from nornir_utils.plugins.functions import write_result\n", + "\n", + "# write_result returns diff between the previous and current file state\n", + "write_result(result, filename=\"out_files/write_result/result.txt\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "5227fd1e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "---- dev1.group_1: dummy_task ** changed : False ------------------------------- INFO\n", + "\n", + "hi!!!\n", + "\n", + "---- dev2.group_1: dummy_task ** changed : False ------------------------------- INFO\n", + "\n", + "hi!!!\n", + "\n", + "---- dev3.group_2: dummy_task ** changed : False ------------------------------- INFO\n", + "\n", + "hi!!!\n", + "\n", + "---- dev4.group_2: dummy_task ** changed : False ------------------------------- INFO\n", + "\n", + "hi!!!\n", + "\n", + "---- dev5.no_group: dummy_task ** changed : False ------------------------------ INFO\n", + "\n", + "hi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "with open(\"out_files/write_result/result.txt\") as f:\n", + " print(f.read())" + ] + }, + { + "cell_type": "markdown", + "id": "a1f86c3a", + "metadata": {}, + "source": [ + "`filename` here is path to file, you want to write the result. `write_result` creates directories from the path with filename, if it's necessary." + ] + }, + { + "cell_type": "markdown", + "id": "c47e072f", + "metadata": {}, + "source": [ + "## Diff\n", + "\n", + "As you can see, `write_result` returns diff between the previous and current file state:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "14c221c4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- out_files/write_result/result.txt\n", + "\n", + "+++ new\n", + "\n", + "@@ -0,0 +1,19 @@\n", + "\n", + "+---- dev1.group_1: smart_task ** changed : False ------------------------------- INFO\n", + "+\n", + "+hihi!!!\n", + "+\n", + "+---- dev2.group_1: smart_task ** changed : False ------------------------------- INFO\n", + "+\n", + "+hihi!!!\n", + "+\n", + "+---- dev3.group_2: smart_task ** changed : False ------------------------------- INFO\n", + "+\n", + "+hihi!!!\n", + "+\n", + "+---- dev4.group_2: smart_task ** changed : False ------------------------------- INFO\n", + "+\n", + "+hihi!!!\n", + "+\n", + "+---- dev5.no_group: smart_task ** changed : False ------------------------------ INFO\n", + "+\n", + "+hihi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "def smart_task(task):\n", + " return \"hihi!!!\"\n", + "\n", + "result = nr.run(task=smart_task)\n", + "\n", + "print(write_result(result, filename=\"out_files/write_result/result.txt\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "d234c3c3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "---- dev1.group_1: smart_task ** changed : False ------------------------------- INFO\n", + "\n", + "hihi!!!\n", + "\n", + "---- dev2.group_1: smart_task ** changed : False ------------------------------- INFO\n", + "\n", + "hihi!!!\n", + "\n", + "---- dev3.group_2: smart_task ** changed : False ------------------------------- INFO\n", + "\n", + "hihi!!!\n", + "\n", + "---- dev4.group_2: smart_task ** changed : False ------------------------------- INFO\n", + "\n", + "hihi!!!\n", + "\n", + "---- dev5.no_group: smart_task ** changed : False ------------------------------ INFO\n", + "\n", + "hihi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "with open(\"out_files/write_result/result.txt\") as f:\n", + " print(f.read())" + ] + }, + { + "cell_type": "markdown", + "id": "231ce6f0", + "metadata": {}, + "source": [ + "## Writing specific data\n", + "\n", + "If the task returns different information, you can also select which ones to write. For instance:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "774eaef7", + "metadata": {}, + "outputs": [], + "source": [ + "from nornir.core.task import Result\n", + "\n", + "def task_with_data(task):\n", + " return Result(host=task.host, result=\"Spaghetti monster\", sw_char=\"Jar Jar Binks\", food=\"hawaiian pizza\", OS=\"windows\")\n", + "\n", + "\n", + "fav_result = nr.run(task=task_with_data)\n", + "\n", + "# write only sw_char, food vars and write diff to diff variable \n", + "diff = write_result(fav_result, filename=\"out_files/write_result/result.txt\", vars=[\"sw_char\", \"food\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "a1ac0f7f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "---- dev1.group_1: task_with_data ** changed : False --------------------------- INFO\n", + "\n", + "Jar Jar Binks\n", + "\n", + "hawaiian pizza\n", + "\n", + "---- dev2.group_1: task_with_data ** changed : False --------------------------- INFO\n", + "\n", + "Jar Jar Binks\n", + "\n", + "hawaiian pizza\n", + "\n", + "---- dev3.group_2: task_with_data ** changed : False --------------------------- INFO\n", + "\n", + "Jar Jar Binks\n", + "\n", + "hawaiian pizza\n", + "\n", + "---- dev4.group_2: task_with_data ** changed : False --------------------------- INFO\n", + "\n", + "Jar Jar Binks\n", + "\n", + "hawaiian pizza\n", + "\n", + "---- dev5.no_group: task_with_data ** changed : False -------------------------- INFO\n", + "\n", + "Jar Jar Binks\n", + "\n", + "hawaiian pizza\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "with open(\"out_files/write_result/result.txt\") as f:\n", + " print(f.read())" + ] + }, + { + "cell_type": "markdown", + "id": "568719c8", + "metadata": {}, + "source": [ + "## Severity\n", + "\n", + "You can also ask `write_result` to write the results only if the severity is equal or superior to the one specified. This is particularly useful if your script is very large and you don't care about intermediate results. For instance:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "6dd24ac3", + "metadata": {}, + "outputs": [], + "source": [ + "import logging\n", + "\n", + "def complex_task(task):\n", + " task.run(task=dummy_task, severity_level=logging.DEBUG)\n", + " task.run(task=dummy_task, severity_level=logging.DEBUG)\n", + " task.run(task=dummy_task, severity_level=logging.DEBUG)\n", + " task.run(task=dummy_task, severity_level=logging.DEBUG)\n", + " task.run(task=dummy_task, severity_level=logging.DEBUG)\n", + " return \"I did a lot of things!!!\"\n", + "\n", + "complex_result = nr.run(task=complex_task)" + ] + }, + { + "cell_type": "markdown", + "id": "36d6d9c4", + "metadata": {}, + "source": [ + "`write_result` will only write `INFO` severity and above by default so if we write the result we should only see the result of the parent task:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "9d33d258", + "metadata": {}, + "outputs": [], + "source": [ + "diff = write_result(complex_result, filename=\"out_files/write_result/result.txt\")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "36f7b1c9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "---- dev1.group_1: complex_task ** changed : False ----------------------------- INFO\n", + "\n", + "I did a lot of things!!!\n", + "\n", + "---- dev2.group_1: complex_task ** changed : False ----------------------------- INFO\n", + "\n", + "I did a lot of things!!!\n", + "\n", + "---- dev3.group_2: complex_task ** changed : False ----------------------------- INFO\n", + "\n", + "I did a lot of things!!!\n", + "\n", + "---- dev4.group_2: complex_task ** changed : False ----------------------------- INFO\n", + "\n", + "I did a lot of things!!!\n", + "\n", + "---- dev5.no_group: complex_task ** changed : False ---------------------------- INFO\n", + "\n", + "I did a lot of things!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "with open(\"out_files/write_result/result.txt\") as f:\n", + " print(f.read())" + ] + }, + { + "cell_type": "markdown", + "id": "86998af3", + "metadata": {}, + "source": [ + "If you wanted to write all the tasks for debugging purposes you could specify it with:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "f9c380ae", + "metadata": {}, + "outputs": [], + "source": [ + "diff = write_result(complex_result, filename=\"out_files/write_result/result.txt\", severity_level=logging.DEBUG)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "3801974b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "---- dev1.group_1: complex_task ** changed : False ----------------------------- INFO\n", + "\n", + "I did a lot of things!!!\n", + "\n", + "---- dev1.group_1: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev1.group_1: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev1.group_1: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev1.group_1: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev1.group_1: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev2.group_1: complex_task ** changed : False ----------------------------- INFO\n", + "\n", + "I did a lot of things!!!\n", + "\n", + "---- dev2.group_1: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev2.group_1: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev2.group_1: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev2.group_1: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev2.group_1: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev3.group_2: complex_task ** changed : False ----------------------------- INFO\n", + "\n", + "I did a lot of things!!!\n", + "\n", + "---- dev3.group_2: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev3.group_2: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev3.group_2: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev3.group_2: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev3.group_2: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev4.group_2: complex_task ** changed : False ----------------------------- INFO\n", + "\n", + "I did a lot of things!!!\n", + "\n", + "---- dev4.group_2: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev4.group_2: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev4.group_2: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev4.group_2: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev4.group_2: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev5.no_group: complex_task ** changed : False ---------------------------- INFO\n", + "\n", + "I did a lot of things!!!\n", + "\n", + "---- dev5.no_group: dummy_task ** changed : False ------------------------------ DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev5.no_group: dummy_task ** changed : False ------------------------------ DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev5.no_group: dummy_task ** changed : False ------------------------------ DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev5.no_group: dummy_task ** changed : False ------------------------------ DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev5.no_group: dummy_task ** changed : False ------------------------------ DEBUG\n", + "\n", + "hi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "with open(\"out_files/write_result/result.txt\") as f:\n", + " print(f.read())" + ] + }, + { + "cell_type": "markdown", + "id": "44d096ef", + "metadata": {}, + "source": [ + "## Write host\n", + "\n", + "`write_host` argument can help you, if you don't need to write hostnames to file:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "fb93e599", + "metadata": {}, + "outputs": [], + "source": [ + "diff = write_result(result, filename=\"out_files/write_result/result.txt\", write_host=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "1675db9a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "---- smart_task ** changed : False --------------------------------------------- INFO\n", + "\n", + "hihi!!!\n", + "\n", + "---- smart_task ** changed : False --------------------------------------------- INFO\n", + "\n", + "hihi!!!\n", + "\n", + "---- smart_task ** changed : False --------------------------------------------- INFO\n", + "\n", + "hihi!!!\n", + "\n", + "---- smart_task ** changed : False --------------------------------------------- INFO\n", + "\n", + "hihi!!!\n", + "\n", + "---- smart_task ** changed : False --------------------------------------------- INFO\n", + "\n", + "hihi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "with open(\"out_files/write_result/result.txt\") as f:\n", + " print(f.read())" + ] + }, + { + "cell_type": "markdown", + "id": "34a824f1", + "metadata": {}, + "source": [ + "## Append\n", + "\n", + "`append` argument allows you to use `a+` or `w+` (default mode) writing mode. As instance:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "62cbce4a", + "metadata": {}, + "outputs": [], + "source": [ + "diff = write_result(fav_result, filename=\"out_files/write_result/result.txt\", append=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "77bfdfba", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "---- smart_task ** changed : False --------------------------------------------- INFO\n", + "\n", + "hihi!!!\n", + "\n", + "---- smart_task ** changed : False --------------------------------------------- INFO\n", + "\n", + "hihi!!!\n", + "\n", + "---- smart_task ** changed : False --------------------------------------------- INFO\n", + "\n", + "hihi!!!\n", + "\n", + "---- smart_task ** changed : False --------------------------------------------- INFO\n", + "\n", + "hihi!!!\n", + "\n", + "---- smart_task ** changed : False --------------------------------------------- INFO\n", + "\n", + "hihi!!!\n", + "\n", + "---- dev1.group_1: task_with_data ** changed : False --------------------------- INFO\n", + "\n", + "Spaghetti monster\n", + "\n", + "---- dev2.group_1: task_with_data ** changed : False --------------------------- INFO\n", + "\n", + "Spaghetti monster\n", + "\n", + "---- dev3.group_2: task_with_data ** changed : False --------------------------- INFO\n", + "\n", + "Spaghetti monster\n", + "\n", + "---- dev4.group_2: task_with_data ** changed : False --------------------------- INFO\n", + "\n", + "Spaghetti monster\n", + "\n", + "---- dev5.no_group: task_with_data ** changed : False -------------------------- INFO\n", + "\n", + "Spaghetti monster\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "with open(\"out_files/write_result/result.txt\") as f:\n", + " print(f.read())" + ] + }, + { + "cell_type": "markdown", + "id": "57f5b76d", + "metadata": {}, + "source": [ + "# No errors\n", + "\n", + "Don't write errors to file with `no_errors` argument. As instance:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "c9d3a814", + "metadata": {}, + "outputs": [], + "source": [ + "def task_with_exception(task):\n", + " return Result(host=task.host, result=\"Something went wrong\", exception=Exception())\n", + "\n", + "\n", + "def task_without_exception(task):\n", + " return \"All is fine\"\n", + "\n", + "def task(task):\n", + " task.run(task=task_with_exception)\n", + " task.run(task=task_without_exception)\n", + " return \"What's happening?\"\n", + "\n", + " \n", + "result_with_exception = nr.run(task=task)\n", + "\n", + "diff = write_result(result_with_exception, filename=\"out_files/write_result/result.txt\")" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "85ecda49", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "---- dev1.group_1: task ** changed : False ------------------------------------- INFO\n", + "\n", + "What's happening?\n", + "\n", + "---- dev1.group_1: task_with_exception ** changed : False ---------------------- INFO\n", + "\n", + "Something went wrong\n", + "\n", + "---- dev1.group_1: task_without_exception ** changed : False ------------------- INFO\n", + "\n", + "All is fine\n", + "\n", + "---- dev2.group_1: task ** changed : False ------------------------------------- INFO\n", + "\n", + "What's happening?\n", + "\n", + "---- dev2.group_1: task_with_exception ** changed : False ---------------------- INFO\n", + "\n", + "Something went wrong\n", + "\n", + "---- dev2.group_1: task_without_exception ** changed : False ------------------- INFO\n", + "\n", + "All is fine\n", + "\n", + "---- dev3.group_2: task ** changed : False ------------------------------------- INFO\n", + "\n", + "What's happening?\n", + "\n", + "---- dev3.group_2: task_with_exception ** changed : False ---------------------- INFO\n", + "\n", + "Something went wrong\n", + "\n", + "---- dev3.group_2: task_without_exception ** changed : False ------------------- INFO\n", + "\n", + "All is fine\n", + "\n", + "---- dev4.group_2: task ** changed : False ------------------------------------- INFO\n", + "\n", + "What's happening?\n", + "\n", + "---- dev4.group_2: task_with_exception ** changed : False ---------------------- INFO\n", + "\n", + "Something went wrong\n", + "\n", + "---- dev4.group_2: task_without_exception ** changed : False ------------------- INFO\n", + "\n", + "All is fine\n", + "\n", + "---- dev5.no_group: task ** changed : False ------------------------------------ INFO\n", + "\n", + "What's happening?\n", + "\n", + "---- dev5.no_group: task_with_exception ** changed : False --------------------- INFO\n", + "\n", + "Something went wrong\n", + "\n", + "---- dev5.no_group: task_without_exception ** changed : False ------------------ INFO\n", + "\n", + "All is fine\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "with open(\"out_files/write_result/result.txt\") as f:\n", + " print(f.read())" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "4cdc3d01", + "metadata": {}, + "outputs": [], + "source": [ + "diff = write_result(result_with_exception, filename=\"out_files/write_result/result.txt\", no_errors=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "391948d1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "---- dev1.group_1: task ** changed : False ------------------------------------- INFO\n", + "\n", + "What's happening?\n", + "\n", + "---- dev1.group_1: task_without_exception ** changed : False ------------------- INFO\n", + "\n", + "All is fine\n", + "\n", + "---- dev2.group_1: task ** changed : False ------------------------------------- INFO\n", + "\n", + "What's happening?\n", + "\n", + "---- dev2.group_1: task_without_exception ** changed : False ------------------- INFO\n", + "\n", + "All is fine\n", + "\n", + "---- dev3.group_2: task ** changed : False ------------------------------------- INFO\n", + "\n", + "What's happening?\n", + "\n", + "---- dev3.group_2: task_without_exception ** changed : False ------------------- INFO\n", + "\n", + "All is fine\n", + "\n", + "---- dev4.group_2: task ** changed : False ------------------------------------- INFO\n", + "\n", + "What's happening?\n", + "\n", + "---- dev4.group_2: task_without_exception ** changed : False ------------------- INFO\n", + "\n", + "All is fine\n", + "\n", + "---- dev5.no_group: task ** changed : False ------------------------------------ INFO\n", + "\n", + "What's happening?\n", + "\n", + "---- dev5.no_group: task_without_exception ** changed : False ------------------ INFO\n", + "\n", + "All is fine\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "with open(\"out_files/write_result/result.txt\") as f:\n", + " print(f.read())" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/source/tutorials/function_write_results.ipynb b/docs/source/tutorials/function_write_results.ipynb new file mode 100644 index 0000000..6f8ae4e --- /dev/null +++ b/docs/source/tutorials/function_write_results.ipynb @@ -0,0 +1,959 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "2d2c17c5", + "metadata": {}, + "source": [ + "# functions: write_results\n", + "\n", + "[write_results](../api/functions.rst#nornir_utils.plugins.functions.write_results) is a function that writes result to files with hostname names and returns list of tuples with hostname + diff between old file and new file:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "03a00337", + "metadata": {}, + "outputs": [], + "source": [ + "from nornir import InitNornir\n", + "\n", + "def dummy_task(task):\n", + " return \"hi!!!\"\n", + "\n", + "nr = InitNornir(\n", + " inventory={\n", + " \"plugin\":\"YAMLInventory\",\n", + " \"options\": {\n", + " \"host_file\": \"data/hosts.yaml\",\n", + " \"group_file\": \"data/groups.yaml\",\n", + " \"defaults_file\": \"data/defaults.yaml\",\n", + " }\n", + " }\n", + ")\n", + "\n", + "result = nr.run(task=dummy_task)" + ] + }, + { + "cell_type": "markdown", + "id": "57e43930", + "metadata": {}, + "source": [ + "Now we could write the results easily with the `write_results` function:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "cb99b478", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('dev1.group_1',\n", + " '--- out_files/write_results/dev1.group_1\\n\\n+++ new\\n\\n@@ -0,0 +1,3 @@\\n\\n+---- dev1.group_1: dummy_task ** changed : False ------------------------------- INFO\\n+\\n+hi!!!'),\n", + " ('dev2.group_1',\n", + " '--- out_files/write_results/dev2.group_1\\n\\n+++ new\\n\\n@@ -0,0 +1,3 @@\\n\\n+---- dev2.group_1: dummy_task ** changed : False ------------------------------- INFO\\n+\\n+hi!!!'),\n", + " ('dev3.group_2',\n", + " '--- out_files/write_results/dev3.group_2\\n\\n+++ new\\n\\n@@ -0,0 +1,3 @@\\n\\n+---- dev3.group_2: dummy_task ** changed : False ------------------------------- INFO\\n+\\n+hi!!!'),\n", + " ('dev4.group_2',\n", + " '--- out_files/write_results/dev4.group_2\\n\\n+++ new\\n\\n@@ -0,0 +1,3 @@\\n\\n+---- dev4.group_2: dummy_task ** changed : False ------------------------------- INFO\\n+\\n+hi!!!'),\n", + " ('dev5.no_group',\n", + " '--- out_files/write_results/dev5.no_group\\n\\n+++ new\\n\\n@@ -0,0 +1,3 @@\\n\\n+---- dev5.no_group: dummy_task ** changed : False ------------------------------ INFO\\n+\\n+hi!!!')]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from nornir_utils.plugins.functions import write_results\n", + "\n", + "write_results(result, dirname=\"out_files/write_results\")" + ] + }, + { + "cell_type": "markdown", + "id": "22400576", + "metadata": {}, + "source": [ + "```text\n", + "$ ls out_files/write_results\n", + "\n", + "dev1.group_1 dev2.group_1 dev3.group_2 dev4.group_2\tdev5.no_group\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "942776f1", + "metadata": {}, + "source": [ + "Let's look at the content of each file:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "5227fd1e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1mout_files/write_results/dev1.group_1\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev1.group_1: dummy_task ** changed : False ------------------------------- INFO\n", + "\n", + "hi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev2.group_1\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev2.group_1: dummy_task ** changed : False ------------------------------- INFO\n", + "\n", + "hi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev3.group_2\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev3.group_2: dummy_task ** changed : False ------------------------------- INFO\n", + "\n", + "hi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev4.group_2\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev4.group_2: dummy_task ** changed : False ------------------------------- INFO\n", + "\n", + "hi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev5.no_group\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev5.no_group: dummy_task ** changed : False ------------------------------ INFO\n", + "\n", + "hi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "import os\n", + "\n", + "dir_path = \"out_files/write_results/\"\n", + "\n", + "for filename in sorted(os.listdir(dir_path)):\n", + " file_path = dir_path + filename\n", + " \n", + " with open(file_path) as f:\n", + " print(\"\\033[1m\" + file_path + \"\\033[0;0m\", f.read(), sep='\\n')" + ] + }, + { + "cell_type": "markdown", + "id": "a1f86c3a", + "metadata": {}, + "source": [ + "`dirname` here is path to directory, you want to write the results for each host. `write_results` creates directories from the path, if it's necessary." + ] + }, + { + "cell_type": "markdown", + "id": "c47e072f", + "metadata": {}, + "source": [ + "## Diff\n", + "\n", + "As you can see, `write_results` returns list of tuples with hostname + diff between the previous and current file state:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "14c221c4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[('dev1.group_1', '--- out_files/write_results/dev1.group_1\\n\\n+++ new\\n\\n@@ -0,0 +1,3 @@\\n\\n+---- dev1.group_1: smart_task ** changed : False ------------------------------- INFO\\n+\\n+hihi!!!'), ('dev2.group_1', '--- out_files/write_results/dev2.group_1\\n\\n+++ new\\n\\n@@ -0,0 +1,3 @@\\n\\n+---- dev2.group_1: smart_task ** changed : False ------------------------------- INFO\\n+\\n+hihi!!!'), ('dev3.group_2', '--- out_files/write_results/dev3.group_2\\n\\n+++ new\\n\\n@@ -0,0 +1,3 @@\\n\\n+---- dev3.group_2: smart_task ** changed : False ------------------------------- INFO\\n+\\n+hihi!!!'), ('dev4.group_2', '--- out_files/write_results/dev4.group_2\\n\\n+++ new\\n\\n@@ -0,0 +1,3 @@\\n\\n+---- dev4.group_2: smart_task ** changed : False ------------------------------- INFO\\n+\\n+hihi!!!'), ('dev5.no_group', '--- out_files/write_results/dev5.no_group\\n\\n+++ new\\n\\n@@ -0,0 +1,3 @@\\n\\n+---- dev5.no_group: smart_task ** changed : False ------------------------------ INFO\\n+\\n+hihi!!!')]\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "def smart_task(task):\n", + " return \"hihi!!!\"\n", + "\n", + "result = nr.run(task=smart_task)\n", + "\n", + "print(write_results(result, dirname=\"out_files/write_results\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "d234c3c3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1mout_files/write_results/dev1.group_1\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev1.group_1: smart_task ** changed : False ------------------------------- INFO\n", + "\n", + "hihi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev2.group_1\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev2.group_1: smart_task ** changed : False ------------------------------- INFO\n", + "\n", + "hihi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev3.group_2\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev3.group_2: smart_task ** changed : False ------------------------------- INFO\n", + "\n", + "hihi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev4.group_2\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev4.group_2: smart_task ** changed : False ------------------------------- INFO\n", + "\n", + "hihi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev5.no_group\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev5.no_group: smart_task ** changed : False ------------------------------ INFO\n", + "\n", + "hihi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "import os\n", + "\n", + "dir_path = \"out_files/write_results/\"\n", + "\n", + "for filename in sorted(os.listdir(dir_path)):\n", + " file_path = dir_path + filename\n", + " \n", + " with open(file_path) as f:\n", + " print(\"\\033[1m\" + file_path + \"\\033[0;0m\", f.read(), sep='\\n')" + ] + }, + { + "cell_type": "markdown", + "id": "231ce6f0", + "metadata": {}, + "source": [ + "## Writing specific data\n", + "\n", + "If the task returns different information, you can also select which ones to write. For instance:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "774eaef7", + "metadata": {}, + "outputs": [], + "source": [ + "from nornir.core.task import Result\n", + "\n", + "def task_with_data(task):\n", + " return Result(host=task.host, result=\"Spaghetti monster\", sw_char=\"Jar Jar Binks\", food=\"hawaiian pizza\", OS=\"windows\")\n", + "\n", + "\n", + "fav_result = nr.run(task=task_with_data)\n", + "\n", + "# write only sw_char, food vars and write diff to diff variable \n", + "diffs = write_results(fav_result, dirname=\"out_files/write_results\", vars=[\"sw_char\", \"food\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "a1ac0f7f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1mout_files/write_results/dev1.group_1\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev1.group_1: task_with_data ** changed : False --------------------------- INFO\n", + "\n", + "Jar Jar Binks\n", + "\n", + "hawaiian pizza\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev2.group_1\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev2.group_1: task_with_data ** changed : False --------------------------- INFO\n", + "\n", + "Jar Jar Binks\n", + "\n", + "hawaiian pizza\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev3.group_2\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev3.group_2: task_with_data ** changed : False --------------------------- INFO\n", + "\n", + "Jar Jar Binks\n", + "\n", + "hawaiian pizza\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev4.group_2\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev4.group_2: task_with_data ** changed : False --------------------------- INFO\n", + "\n", + "Jar Jar Binks\n", + "\n", + "hawaiian pizza\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev5.no_group\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev5.no_group: task_with_data ** changed : False -------------------------- INFO\n", + "\n", + "Jar Jar Binks\n", + "\n", + "hawaiian pizza\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "import os\n", + "\n", + "dir_path = \"out_files/write_results/\"\n", + "\n", + "for filename in sorted(os.listdir(dir_path)):\n", + " file_path = dir_path + filename\n", + " \n", + " with open(file_path) as f:\n", + " print(\"\\033[1m\" + file_path + \"\\033[0;0m\", f.read(), sep='\\n')" + ] + }, + { + "cell_type": "markdown", + "id": "568719c8", + "metadata": {}, + "source": [ + "## Severity\n", + "\n", + "You can also ask `write_results` to write the results only if the severity is equal or superior to the one specified. This is particularly useful if your script is very large and you don't care about intermediate results. For instance:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "6dd24ac3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AggregatedResult (complex_task): {'dev1.group_1': MultiResult: [Result: \"complex_task\", Result: \"dummy_task\", Result: \"dummy_task\", Result: \"dummy_task\", Result: \"dummy_task\", Result: \"dummy_task\"], 'dev2.group_1': MultiResult: [Result: \"complex_task\", Result: \"dummy_task\", Result: \"dummy_task\", Result: \"dummy_task\", Result: \"dummy_task\", Result: \"dummy_task\"], 'dev3.group_2': MultiResult: [Result: \"complex_task\", Result: \"dummy_task\", Result: \"dummy_task\", Result: \"dummy_task\", Result: \"dummy_task\", Result: \"dummy_task\"], 'dev4.group_2': MultiResult: [Result: \"complex_task\", Result: \"dummy_task\", Result: \"dummy_task\", Result: \"dummy_task\", Result: \"dummy_task\", Result: \"dummy_task\"], 'dev5.no_group': MultiResult: [Result: \"complex_task\", Result: \"dummy_task\", Result: \"dummy_task\", Result: \"dummy_task\", Result: \"dummy_task\", Result: \"dummy_task\"]}" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import logging\n", + "\n", + "def complex_task(task):\n", + " task.run(task=dummy_task, severity_level=logging.DEBUG)\n", + " task.run(task=dummy_task, severity_level=logging.DEBUG)\n", + " task.run(task=dummy_task, severity_level=logging.DEBUG)\n", + " task.run(task=dummy_task, severity_level=logging.DEBUG)\n", + " task.run(task=dummy_task, severity_level=logging.DEBUG)\n", + " return \"I did a lot of things!!!\"\n", + "\n", + "complex_result = nr.run(task=complex_task)\n", + "complex_result" + ] + }, + { + "cell_type": "markdown", + "id": "36d6d9c4", + "metadata": {}, + "source": [ + "`write_results` will only write `INFO` severity and above by default so if we write the result we should only see the result of the parent task:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "9d33d258", + "metadata": {}, + "outputs": [], + "source": [ + "diffs = write_results(complex_result, dirname=\"out_files/write_results\")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "36f7b1c9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1mout_files/write_results/dev1.group_1\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev1.group_1: complex_task ** changed : False ----------------------------- INFO\n", + "\n", + "I did a lot of things!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev2.group_1\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev2.group_1: complex_task ** changed : False ----------------------------- INFO\n", + "\n", + "I did a lot of things!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev3.group_2\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev3.group_2: complex_task ** changed : False ----------------------------- INFO\n", + "\n", + "I did a lot of things!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev4.group_2\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev4.group_2: complex_task ** changed : False ----------------------------- INFO\n", + "\n", + "I did a lot of things!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev5.no_group\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev5.no_group: complex_task ** changed : False ---------------------------- INFO\n", + "\n", + "I did a lot of things!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "import os\n", + "\n", + "dir_path = \"out_files/write_results/\"\n", + "\n", + "for filename in sorted(os.listdir(dir_path)):\n", + " file_path = dir_path + filename\n", + " \n", + " with open(file_path) as f:\n", + " print(\"\\033[1m\" + file_path + \"\\033[0;0m\", f.read(), sep='\\n')" + ] + }, + { + "cell_type": "markdown", + "id": "86998af3", + "metadata": {}, + "source": [ + "If you wanted to write all the tasks for debugging purposes you could specify it with:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "f9c380ae", + "metadata": {}, + "outputs": [], + "source": [ + "diffs = write_results(complex_result, dirname=\"out_files/write_results\", severity_level=logging.DEBUG)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "3801974b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1mout_files/write_results/dev1.group_1\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev1.group_1: complex_task ** changed : False ----------------------------- INFO\n", + "\n", + "I did a lot of things!!!\n", + "\n", + "---- dev1.group_1: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev1.group_1: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev1.group_1: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev1.group_1: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev1.group_1: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev2.group_1\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev2.group_1: complex_task ** changed : False ----------------------------- INFO\n", + "\n", + "I did a lot of things!!!\n", + "\n", + "---- dev2.group_1: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev2.group_1: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev2.group_1: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev2.group_1: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev2.group_1: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev3.group_2\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev3.group_2: complex_task ** changed : False ----------------------------- INFO\n", + "\n", + "I did a lot of things!!!\n", + "\n", + "---- dev3.group_2: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev3.group_2: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev3.group_2: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev3.group_2: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev3.group_2: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev4.group_2\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev4.group_2: complex_task ** changed : False ----------------------------- INFO\n", + "\n", + "I did a lot of things!!!\n", + "\n", + "---- dev4.group_2: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev4.group_2: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev4.group_2: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev4.group_2: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev4.group_2: dummy_task ** changed : False ------------------------------- DEBUG\n", + "\n", + "hi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev5.no_group\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev5.no_group: complex_task ** changed : False ---------------------------- INFO\n", + "\n", + "I did a lot of things!!!\n", + "\n", + "---- dev5.no_group: dummy_task ** changed : False ------------------------------ DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev5.no_group: dummy_task ** changed : False ------------------------------ DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev5.no_group: dummy_task ** changed : False ------------------------------ DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev5.no_group: dummy_task ** changed : False ------------------------------ DEBUG\n", + "\n", + "hi!!!\n", + "\n", + "---- dev5.no_group: dummy_task ** changed : False ------------------------------ DEBUG\n", + "\n", + "hi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "import os\n", + "\n", + "dir_path = \"out_files/write_results/\"\n", + "\n", + "for filename in sorted(os.listdir(dir_path)):\n", + " file_path = dir_path + filename\n", + " \n", + " with open(file_path) as f:\n", + " print(\"\\033[1m\" + file_path + \"\\033[0;0m\", f.read(), sep='\\n')" + ] + }, + { + "cell_type": "markdown", + "id": "44d096ef", + "metadata": {}, + "source": [ + "## Write host\n", + "\n", + "`write_host` argument can help you, if you don't need to write hostnames to files:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "fb93e599", + "metadata": {}, + "outputs": [], + "source": [ + "diff = write_results(result, dirname=\"out_files/write_results\", write_host=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "1675db9a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1mout_files/write_results/dev1.group_1\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- smart_task ** changed : False --------------------------------------------- INFO\n", + "\n", + "hihi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev2.group_1\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- smart_task ** changed : False --------------------------------------------- INFO\n", + "\n", + "hihi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev3.group_2\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- smart_task ** changed : False --------------------------------------------- INFO\n", + "\n", + "hihi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev4.group_2\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- smart_task ** changed : False --------------------------------------------- INFO\n", + "\n", + "hihi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev5.no_group\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- smart_task ** changed : False --------------------------------------------- INFO\n", + "\n", + "hihi!!!\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "import os\n", + "\n", + "dir_path = \"out_files/write_results/\"\n", + "\n", + "for filename in sorted(os.listdir(dir_path)):\n", + " file_path = dir_path + filename\n", + " \n", + " with open(file_path) as f:\n", + " print(\"\\033[1m\" + file_path + \"\\033[0;0m\", f.read(), sep='\\n')" + ] + }, + { + "cell_type": "markdown", + "id": "34a824f1", + "metadata": {}, + "source": [ + "## Append\n", + "\n", + "`append` argument allows you to use `a+` or `w+` (default mode) writing mode. As instance:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "62cbce4a", + "metadata": {}, + "outputs": [], + "source": [ + "diffs = write_results(fav_result, dirname=\"out_files/write_results\", append=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "77bfdfba", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1mout_files/write_results/dev1.group_1\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- smart_task ** changed : False --------------------------------------------- INFO\n", + "\n", + "hihi!!!\n", + "\n", + "---- dev1.group_1: task_with_data ** changed : False --------------------------- INFO\n", + "\n", + "Spaghetti monster\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev2.group_1\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- smart_task ** changed : False --------------------------------------------- INFO\n", + "\n", + "hihi!!!\n", + "\n", + "---- dev2.group_1: task_with_data ** changed : False --------------------------- INFO\n", + "\n", + "Spaghetti monster\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev3.group_2\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- smart_task ** changed : False --------------------------------------------- INFO\n", + "\n", + "hihi!!!\n", + "\n", + "---- dev3.group_2: task_with_data ** changed : False --------------------------- INFO\n", + "\n", + "Spaghetti monster\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev4.group_2\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- smart_task ** changed : False --------------------------------------------- INFO\n", + "\n", + "hihi!!!\n", + "\n", + "---- dev4.group_2: task_with_data ** changed : False --------------------------- INFO\n", + "\n", + "Spaghetti monster\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev5.no_group\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- smart_task ** changed : False --------------------------------------------- INFO\n", + "\n", + "hihi!!!\n", + "\n", + "---- dev5.no_group: task_with_data ** changed : False -------------------------- INFO\n", + "\n", + "Spaghetti monster\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "import os\n", + "\n", + "dir_path = \"out_files/write_results/\"\n", + "\n", + "for filename in sorted(os.listdir(dir_path)):\n", + " file_path = dir_path + filename\n", + " \n", + " with open(file_path) as f:\n", + " print(\"\\033[1m\" + file_path + \"\\033[0;0m\", f.read(), sep='\\n')" + ] + }, + { + "cell_type": "markdown", + "id": "57f5b76d", + "metadata": {}, + "source": [ + "# No errors\n", + "\n", + "Don't write errors to files with `no_errors` argument. As instance:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "c9d3a814", + "metadata": {}, + "outputs": [], + "source": [ + "def task_with_exception(task):\n", + " return Result(host=task.host, result=\"Something went wrong\", exception=Exception())\n", + "\n", + "\n", + "def task_without_exception(task):\n", + " return \"All is fine\"\n", + "\n", + "def task(task):\n", + " task.run(task=task_with_exception)\n", + " task.run(task=task_without_exception)\n", + " return \"What's happening?\"\n", + "\n", + " \n", + "result_with_exception = nr.run(task=task)\n", + "\n", + "diffs = write_results(result_with_exception, dirname=\"out_files/write_results\")" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "85ecda49", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1mout_files/write_results/dev1.group_1\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev1.group_1: task ** changed : False ------------------------------------- INFO\n", + "\n", + "What's happening?\n", + "\n", + "---- dev1.group_1: task_with_exception ** changed : False ---------------------- INFO\n", + "\n", + "Something went wrong\n", + "\n", + "---- dev1.group_1: task_without_exception ** changed : False ------------------- INFO\n", + "\n", + "All is fine\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev2.group_1\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev2.group_1: task ** changed : False ------------------------------------- INFO\n", + "\n", + "What's happening?\n", + "\n", + "---- dev2.group_1: task_with_exception ** changed : False ---------------------- INFO\n", + "\n", + "Something went wrong\n", + "\n", + "---- dev2.group_1: task_without_exception ** changed : False ------------------- INFO\n", + "\n", + "All is fine\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev3.group_2\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev3.group_2: task ** changed : False ------------------------------------- INFO\n", + "\n", + "What's happening?\n", + "\n", + "---- dev3.group_2: task_with_exception ** changed : False ---------------------- INFO\n", + "\n", + "Something went wrong\n", + "\n", + "---- dev3.group_2: task_without_exception ** changed : False ------------------- INFO\n", + "\n", + "All is fine\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev4.group_2\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev4.group_2: task ** changed : False ------------------------------------- INFO\n", + "\n", + "What's happening?\n", + "\n", + "---- dev4.group_2: task_with_exception ** changed : False ---------------------- INFO\n", + "\n", + "Something went wrong\n", + "\n", + "---- dev4.group_2: task_without_exception ** changed : False ------------------- INFO\n", + "\n", + "All is fine\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev5.no_group\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev5.no_group: task ** changed : False ------------------------------------ INFO\n", + "\n", + "What's happening?\n", + "\n", + "---- dev5.no_group: task_with_exception ** changed : False --------------------- INFO\n", + "\n", + "Something went wrong\n", + "\n", + "---- dev5.no_group: task_without_exception ** changed : False ------------------ INFO\n", + "\n", + "All is fine\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "import os\n", + "\n", + "dir_path = \"out_files/write_results/\"\n", + "\n", + "for filename in sorted(os.listdir(dir_path)):\n", + " file_path = dir_path + filename\n", + " \n", + " with open(file_path) as f:\n", + " print(\"\\033[1m\" + file_path + \"\\033[0;0m\", f.read(), sep='\\n')" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "4cdc3d01", + "metadata": {}, + "outputs": [], + "source": [ + "diffs = write_results(result_with_exception, dirname=\"out_files/write_results\", no_errors=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "391948d1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1mout_files/write_results/dev1.group_1\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev1.group_1: task ** changed : False ------------------------------------- INFO\n", + "\n", + "What's happening?\n", + "\n", + "---- dev1.group_1: task_without_exception ** changed : False ------------------- INFO\n", + "\n", + "All is fine\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev2.group_1\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev2.group_1: task ** changed : False ------------------------------------- INFO\n", + "\n", + "What's happening?\n", + "\n", + "---- dev2.group_1: task_without_exception ** changed : False ------------------- INFO\n", + "\n", + "All is fine\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev3.group_2\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev3.group_2: task ** changed : False ------------------------------------- INFO\n", + "\n", + "What's happening?\n", + "\n", + "---- dev3.group_2: task_without_exception ** changed : False ------------------- INFO\n", + "\n", + "All is fine\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev4.group_2\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev4.group_2: task ** changed : False ------------------------------------- INFO\n", + "\n", + "What's happening?\n", + "\n", + "---- dev4.group_2: task_without_exception ** changed : False ------------------- INFO\n", + "\n", + "All is fine\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[1mout_files/write_results/dev5.no_group\u001b[0;0m\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m---- dev5.no_group: task ** changed : False ------------------------------------ INFO\n", + "\n", + "What's happening?\n", + "\n", + "---- dev5.no_group: task_without_exception ** changed : False ------------------ INFO\n", + "\n", + "All is fine\u001b[0m\u001b[0m\u001b[0m\n", + "\u001b[0m\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "import os\n", + "\n", + "dir_path = \"out_files/write_results/\"\n", + "\n", + "for filename in sorted(os.listdir(dir_path)):\n", + " file_path = dir_path + filename\n", + " \n", + " with open(file_path) as f:\n", + " print(\"\\033[1m\" + file_path + \"\\033[0;0m\", f.read(), sep='\\n')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/nornir_utils/plugins/functions/__init__.py b/nornir_utils/plugins/functions/__init__.py index a2acbf4..da1bdd4 100644 --- a/nornir_utils/plugins/functions/__init__.py +++ b/nornir_utils/plugins/functions/__init__.py @@ -1,3 +1,16 @@ -from .print_result import print_result, print_title +from .print_result import ( + print_result, + print_title, +) -__all__ = ("print_result", "print_title") +from .print_stat import print_stat +from .write_result import write_result +from .write_results import write_results + +__all__ = ( + "print_result", + "print_title", + "print_stat", + "write_result", + "write_results", +) diff --git a/nornir_utils/plugins/functions/print_stat.py b/nornir_utils/plugins/functions/print_stat.py new file mode 100644 index 0000000..8452c29 --- /dev/null +++ b/nornir_utils/plugins/functions/print_stat.py @@ -0,0 +1,96 @@ +import threading +from typing import Tuple +from nornir_utils.plugins.functions.print_result import _get_color + +from colorama import Fore, Style, init + +from nornir.core.task import AggregatedResult, MultiResult, Result + + +LOCK = threading.Lock() + + +init(autoreset=True, strip=False) + + +def _print_individual_stat( + result: Result, + res_sum: int = 0, + ch_sum: int = 0, + f_sum: int = 0, +) -> Tuple[int, int, int]: + + f, ch = (result.failed, result.changed) + + res_sum += 1 + ch_sum += int(ch) + f_sum += int(f) + + color = _get_color(result, f) + msg = "{:<35} ok={:<15} changed={:<15} failed={:<15}".format( + result.name, not f, ch, f + ) + print(" {}{}{}".format(Style.BRIGHT, color, msg)) + return res_sum, ch_sum, f_sum + + +def _print_stat( + result: Result, + res_sum: int = 0, + ch_sum: int = 0, + f_sum: int = 0, +) -> Tuple[int, int, int]: + + if isinstance(result, AggregatedResult): + msg = result.name + print("{}{}{}{}".format(Style.BRIGHT, Fore.CYAN, msg, "*" * (80 - len(msg)))) + for host, host_data in result.items(): + msg_host = "* {} ".format(host) + print( + "{}{}{}{}".format( + Style.BRIGHT, + Fore.BLUE, + msg_host, + "*" * (80 - len(msg_host)), + ) + ) + res_sum, ch_sum, f_sum = _print_stat(host_data, res_sum, ch_sum, f_sum) + elif isinstance(result, MultiResult): + for res in result: + res_sum, ch_sum, f_sum = _print_stat(res, res_sum, ch_sum, f_sum) + elif isinstance(result, Result): + res_sum, ch_sum, f_sum = _print_individual_stat(result, res_sum, ch_sum, f_sum) + + return res_sum, ch_sum, f_sum + + +def print_stat(result: Result) -> None: + """ + Prints statistic for `nornir.core.task.Result` object + + Args: + result: From a previous task + + Returns: + N/A + + Raises: + N/A + """ + LOCK.acquire() + try: + res_sum, ch_sum, f_sum = _print_stat( + result, + res_sum=0, + ch_sum=0, + f_sum=0, + ) + print() + for state, summary, color in zip( + ("OK", "CHANGED", "FAILED"), + (res_sum - f_sum, ch_sum, f_sum), + (Fore.GREEN, Fore.YELLOW, Fore.RED), + ): + print("{}{}{:<8}: {}".format(Style.BRIGHT, color, state, summary)) + finally: + LOCK.release() diff --git a/nornir_utils/plugins/functions/write_result.py b/nornir_utils/plugins/functions/write_result.py new file mode 100644 index 0000000..9713261 --- /dev/null +++ b/nornir_utils/plugins/functions/write_result.py @@ -0,0 +1,182 @@ +import os +import logging +import threading +import json +from pathlib import Path +from typing import List, IO, AnyStr +from nornir_utils.plugins.tasks.files.write_file import _generate_diff +from nornir.core.task import AggregatedResult, MultiResult, Result + + +LOCK = threading.Lock() + + +def _write_individual_result( + result: Result, + io: IO[AnyStr], + attrs: List[str], + failed: bool, + severity_level: int, + task_group: bool = False, + write_host: bool = False, + no_errors: bool = False, + content: List[str] = [], +) -> List[str]: + + # ignore results with a specific severity_level + if result.severity_level < severity_level: + return content + + # ignore results with errors + if no_errors: + if result.exception: + return content + + subtitle = ( + "" if result.changed is None else " ** changed : {} ".format(result.changed) + ) + level_name = logging.getLevelName(result.severity_level) + symbol = "v" if task_group else "-" + host = ( + f"{result.host.name}: " + if (write_host and result.host and result.host.name) + else "" + ) + msg = "{} {}{}{}".format(symbol * 4, host, result.name, subtitle) + content.append("{}{} {}".format(msg, symbol * (80 - len(msg)), level_name)) + for attribute in attrs: + x = getattr(result, attribute, "") + if isinstance(x, BaseException): + # for consistency between py3.6 and py3.7 + content.append(f"{x.__class__.__name__}{x.args}") + elif x and not isinstance(x, str): + try: + content.append( + json.dumps(x, indent=2, ensure_ascii=False).encode("utf-8").decode() + ) + except TypeError: + content.append(str(x)) + elif x: + content.append(x) + + return content + + +def _write_result( + result: Result, + io: IO[AnyStr], + attrs: List[str] = None, + failed: bool = False, + severity_level: int = logging.INFO, + write_host: bool = False, + no_errors: bool = False, + content: List[str] = [], +) -> List[str]: + + attrs = attrs or ["diff", "result", "stdout"] + if isinstance(attrs, str): + attrs = [attrs] + + if isinstance(result, AggregatedResult): + for host_data in result.values(): + content = _write_result( + host_data, + io, + attrs, + failed, + severity_level, + write_host, + no_errors=no_errors, + content=content, + ) + elif isinstance(result, MultiResult): + for r in result: + content = _write_result( + r, + io, + attrs, + failed, + severity_level, + write_host, + no_errors=no_errors, + content=content, + ) + elif isinstance(result, Result): + content = _write_individual_result( + result, + io, + attrs, + failed, + severity_level, + write_host=write_host, + no_errors=no_errors, + content=content, + ) + + return content + + +def write_result( + result: Result, + filename: str, + vars: List[str] = None, + failed: bool = False, + severity_level: int = logging.INFO, + write_host: bool = True, + append: bool = False, + no_errors: bool = False, +) -> str: + """ + Writes an object of type `nornir.core.task.Result` to file + + Args: + result: From a previous task + (Result or AggregatedResult or MultiResult) + filename: File you want to write the result + vars: Which attributes you want to write + (see ``class Result`` attributes) + failed: If ``True`` assume the task failed + severity_level: Write only errors with this severity level or higher + write_host: Write hostname to file + append: "a+" if ``True`` or "w+" if ``False`` + no_errors: Don't write results with errors + + Returns: + str: Diff between previous file state and new file state + + Raises: + N/A + """ + + dirname = os.path.dirname(filename) + Path(dirname).mkdir(parents=True, exist_ok=True) + + mode = "a+" if append else "w+" + + LOCK.acquire() + + try: + with open(filename, mode=mode) as f: + content: List[str] = _write_result( + result, + io=f, + attrs=vars, + failed=failed, + severity_level=severity_level, + write_host=write_host, + no_errors=no_errors, + content=[], + ) + + lf = "\n\n" if Path(filename).stat().st_size != 0 and append else "" + + lines = [line.strip() for line in content] + line = lf + "\n\n".join(lines) + + diff = _generate_diff(filename, line, append) + + f.write(line) + + return diff + finally: + LOCK.release() diff --git a/nornir_utils/plugins/functions/write_results.py b/nornir_utils/plugins/functions/write_results.py new file mode 100644 index 0000000..194605f --- /dev/null +++ b/nornir_utils/plugins/functions/write_results.py @@ -0,0 +1,203 @@ +import os +import json +import logging +import threading +from pathlib import Path +from typing import List, Tuple, Dict +from nornir.core.task import AggregatedResult, MultiResult, Result +from nornir_utils.plugins.tasks.files.write_file import _generate_diff + + +LOCK = threading.Lock() + + +def _write_individual_result( + result: Result, + dirname: str, + attrs: List[str], + failed: bool, + severity_level: int, + task_group: bool = False, + write_host: bool = False, + no_errors: bool = False, + append: bool = False, + content: Dict[str, List[str]] = {}, +) -> Dict[str, List[str]]: + + individual_result = [] + + # ignore results with a specific severity_level + if result.severity_level < severity_level: + return content + + # ignore results with errors + if no_errors and result.exception: + return content + + filename = str(result.host) + + subtitle = ( + "" if result.changed is None else " ** changed : {} ".format(result.changed) + ) + level_name = logging.getLevelName(result.severity_level) + symbol = "v" if task_group else "-" + host = f"{filename}: " if write_host else "" + msg = "{} {}{}{}".format(symbol * 4, host, result.name, subtitle) + individual_result.append( + "{}{} {}".format(msg, symbol * (80 - len(msg)), level_name) + ) + for attribute in attrs: + x = getattr(result, attribute, "") + if isinstance(x, BaseException): + # for consistency between py3.6 and py3.7 + individual_result.append(f"{x.__class__.__name__}{x.args}") + elif x and not isinstance(x, str): + try: + individual_result.append( + json.dumps(x, indent=2, ensure_ascii=False).encode("utf-8").decode() + ) + except TypeError: + individual_result.append(str(x)) + elif x: + individual_result.append(x) + + lines = [line.strip() for line in individual_result] + content[filename].extend(lines) + + return content + + +def _write_results( + result: Result, + dirname: str, + attrs: List[str] = None, + failed: bool = False, + severity_level: int = logging.INFO, + write_host: bool = False, + no_errors: bool = False, + append: bool = False, + content: Dict[str, List[str]] = {}, +) -> Dict[str, List[str]]: + + attrs = attrs or ["diff", "result", "stdout"] + if isinstance(attrs, str): + attrs = [attrs] + + if isinstance(result, AggregatedResult): + for host_data in result.values(): + content = _write_results( + host_data, + dirname, + attrs, + failed, + severity_level, + write_host, + no_errors=no_errors, + append=append, + content=content, + ) + elif isinstance(result, MultiResult): + for r in result: + # collect the results to dict, where key = result.host.name + if result.host and result.host.name: + filename = result.host.name + if filename not in content.keys(): + content[filename] = [] + else: + continue + content = _write_results( + r, + dirname, + attrs, + failed, + severity_level, + write_host, + no_errors=no_errors, + append=append, + content=content, + ) + elif isinstance(result, Result): + content = _write_individual_result( + result, + dirname, + attrs, + failed, + severity_level, + write_host=write_host, + no_errors=no_errors, + append=append, + content=content, + ) + + return content + + +def write_results( + result: Result, + dirname: str, + vars: List[str] = None, + failed: bool = False, + severity_level: int = logging.INFO, + write_host: bool = True, + no_errors: bool = False, + append: bool = False, +) -> List[Tuple[str, str]]: + """ + Writes an object of type `nornir.core.task.Result` + to files with hostname names + + Args: + result: From a previous task(Result or AggregatedResult or MultiResult) + dirname: Directory you want to write into + vars: Which attributes you want to write + (see ``class Result`` attributes) + failed: If ``True`` assume the task failed + severity_level: Write only errors with this severity level or higher + write_host: Write hostname to file + no_errors: Don't write results with errors + append: "a+" if ``True`` or "w+" if ``False`` + + Returns: + List[Tuple[str, str]]: List of tuples with hostname + diff + between previous file state and new file state + + Raises: + N/A + """ + Path(dirname).mkdir(parents=True, exist_ok=True) + + mode = "a+" if append else "w+" + + LOCK.acquire() + + try: + content: Dict[str, List[str]] = _write_results( + result, + dirname, + attrs=vars, + failed=failed, + severity_level=severity_level, + write_host=write_host, + no_errors=no_errors, + append=append, + content={}, + ) + + diffs = [] + + for name, res in content.items(): + path = os.path.join(dirname, name) + with open(path, mode=mode) as f: + line = "\n\n".join(res) + + lf = "\n\n" if Path(path).stat().st_size != 0 and append else "" + + line = lf + line + + diff = _generate_diff(path, line, append) + + f.write(line) + diffs.append((name, diff)) + return diffs + finally: + LOCK.release()