{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# Initializing objects for later use\n", "from nornir import InitNornir\n", "from nornir_utils.plugins.functions import print_result\n", "\n", "nr = InitNornir(config_file=\"config.yaml\")\n", "# filtering objects to simplify output\n", "nr = nr.filter(site=\"cmh\", role=\"host\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Tasks\n", "\n", "Now that you know how to initialize nornir and work with the inventory let's see how we can leverage it to run tasks on groups of hosts.\n", "\n", "A task is a reusable piece of code that implements some functionality for a single host. In python terms it is a function that takes a [Task](../api/nornir/core/task.html#nornir.core.task.Task) as first paramater and returns a [Result](../api/nornir/core/task.html#nornir.core.task.Result).\n", "\n", "For instance:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "from nornir.core.task import Task, Result\n", "\n", "def hello_world(task: Task) -> Result:\n", " return Result(\n", " host=task.host,\n", " result=f\"{task.host.name} says hello world!\"\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To execute a task you can use the [run](../api/nornir/core/__init__.html#nornir.core.__init__.Nornir.run) method:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[1m\u001b[36mhello_world*********************************************************************\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[34m* host1.cmh ** changed : False *************************************************\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[32mvvvv hello_world ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO\u001b[0m\n", "\u001b[0mhost1.cmh says hello world!\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[32m^^^^ END hello_world ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[34m* host2.cmh ** changed : False *************************************************\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[32mvvvv hello_world ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO\u001b[0m\n", "\u001b[0mhost2.cmh says hello world!\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[32m^^^^ END hello_world ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m\n", "\u001b[0m" ] } ], "source": [ "result = nr.run(task=hello_world)\n", "print_result(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Tasks can also take any number of arguments to extend their functionality. For instance:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "def say(task: Task, text: str) -> Result:\n", " return Result(\n", " host=task.host,\n", " result=f\"{task.host.name} says {text}\"\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which can then be called like before but specifying the values for the extra argument:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[1m\u001b[36mSaying goodbye in a very friendly manner****************************************\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[34m* host1.cmh ** changed : False *************************************************\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[32mvvvv Saying goodbye in a very friendly manner ** changed : False vvvvvvvvvvvvvvv INFO\u001b[0m\n", "\u001b[0mhost1.cmh says buhbye!\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[32m^^^^ END Saying goodbye in a very friendly manner ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[34m* host2.cmh ** changed : False *************************************************\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[32mvvvv Saying goodbye in a very friendly manner ** changed : False vvvvvvvvvvvvvvv INFO\u001b[0m\n", "\u001b[0mhost2.cmh says buhbye!\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[32m^^^^ END Saying goodbye in a very friendly manner ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m\n", "\u001b[0m" ] } ], "source": [ "result = nr.run(\n", " name=\"Saying goodbye in a very friendly manner\",\n", " task=say,\n", " text=\"buhbye!\"\n", ")\n", "print_result(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that we passed a `name` argument to the `run` function. This argument is parameter and it allows us to give the task a descriptive name. If it's not specified the function name is used instead.\n", "\n", "\n", "## Grouping tasks\n", "\n", "A task can also call other tasks. This is useful as it can allow you to build more complex functionality by combining smaller building blocks. To illustrate this, let's first define a new task:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "def count(task: Task, number: int) -> Result:\n", " return Result(\n", " host=task.host,\n", " result=f\"{[n for n in range(0, number)]}\"\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, let's combine this with the `say` function we defined earlier to implement a more complex workflow:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "def greet_and_count(task: Task, number: int) -> Result:\n", " task.run(\n", " name=\"Greeting is the polite thing to do\",\n", " task=say,\n", " text=\"hi!\",\n", " )\n", " \n", " task.run(\n", " name=\"Counting beans\",\n", " task=count,\n", " number=number,\n", " )\n", " task.run(\n", " name=\"We should say bye too\",\n", " task=say,\n", " text=\"bye!\",\n", " )\n", "\n", " # let's inform if we counted even or odd times\n", " even_or_odds = \"even\" if number % 2 == 0 else \"odd\"\n", " return Result(\n", " host=task.host,\n", " result=f\"{task.host} counted {even_or_odds} times!\",\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is worth noting a couple of things:\n", "\n", "1. The first time we call the `say` function we hardcode the text to \"hi!\" while the second time we do it (the last action) we hardcode it to \"bye!\"\n", "2. When calling `count` we pass the parameter that we also specified to the parent task `greet_and_count`. That way we can make the parts we are interested in dynamic\n", "3. Finally, we return a `Result` object with some meaningful information about the whole workflow\n", "\n", "Now that we have the grouped task we can call is as with any other regular task:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[1m\u001b[36mCounting to 5 while being very polite*******************************************\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[34m* host1.cmh ** changed : False *************************************************\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[32mvvvv Counting to 5 while being very polite ** changed : False vvvvvvvvvvvvvvvvvv INFO\u001b[0m\n", "\u001b[0mhost1.cmh counted odd times!\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[32m---- Greeting is the polite thing to do ** changed : False --------------------- INFO\u001b[0m\n", "\u001b[0mhost1.cmh says hi!\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[32m---- Counting beans ** changed : False ----------------------------------------- INFO\u001b[0m\n", "\u001b[0m[0, 1, 2, 3, 4]\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[32m---- We should say bye too ** changed : False ---------------------------------- INFO\u001b[0m\n", "\u001b[0mhost1.cmh says bye!\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[32m^^^^ END Counting to 5 while being very polite ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[34m* host2.cmh ** changed : False *************************************************\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[32mvvvv Counting to 5 while being very polite ** changed : False vvvvvvvvvvvvvvvvvv INFO\u001b[0m\n", "\u001b[0mhost2.cmh counted odd times!\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[32m---- Greeting is the polite thing to do ** changed : False --------------------- INFO\u001b[0m\n", "\u001b[0mhost2.cmh says hi!\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[32m---- Counting beans ** changed : False ----------------------------------------- INFO\u001b[0m\n", "\u001b[0m[0, 1, 2, 3, 4]\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[32m---- We should say bye too ** changed : False ---------------------------------- INFO\u001b[0m\n", "\u001b[0mhost2.cmh says bye!\u001b[0m\n", "\u001b[0m\u001b[1m\u001b[32m^^^^ END Counting to 5 while being very polite ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m\n", "\u001b[0m" ] } ], "source": [ "result = nr.run(\n", " name=\"Counting to 5 while being very polite\",\n", " task=greet_and_count,\n", " number=5,\n", ")\n", "print_result(result)" ] } ], "metadata": { "celltoolbar": "Edit 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.7" } }, "nbformat": 4, "nbformat_minor": 2 }