<jupyter_start><jupyter_text>Failed TasksSometimes tasks can fail. Let's see how to deal with failed tasks in nornir.Let's start as usual with the needed boilerplate:<jupyter_code>from nornir.core import InitNornir
from nornir.plugins.tasks import networking, text
from nornir.plugins.functions.text import print_result

nr = InitNornir(config_file="config.yaml")
cmh = nr.filter(site="cmh", type="network_device")<jupyter_output><empty_output><jupyter_text>Now, as an example we are going to use a similar task group like the one we used in the previous tutorial:<jupyter_code>def basic_configuration(task):
    # Transform inventory data to configuration via a template file
    r = task.run(task=text.template_file,
                 name="Base Configuration",
                 template="base.j2",
                 path=f"templates/junos")

    # Save the compiled configuration into a host variable
    task.host["config"] = r.result

    # Deploy that configuration to the device using NAPALM
    task.run(task=networking.napalm_configure,
             name="Loading Configuration on the device",
             replace=False,
             configuration=task.host["config"])<jupyter_output><empty_output><jupyter_text>Note that the path is hardcoded to templates/junos, this should cause an error when trying to apply the configuration to the EOS devices. Let's see what happens:<jupyter_code>result = cmh.run(task=basic_configuration)<jupyter_output><empty_output><jupyter_text>Let's inspect the object:<jupyter_code>result.failed
result.failed_hosts
result['spine00.cmh'][1].exception<jupyter_output><empty_output><jupyter_text>As you can see, the result object is aware something went wrong and you can inspect the errors if you so desire.You can also using the `print_result` function on it:<jupyter_code>print_result(result)<jupyter_output>[1m[36mbasic_configuration*************************************************************[0m
[0m[1m[34m* leaf00.cmh ** changed : False ************************************************[0m
[0m[1m[31mvvvv basic_configuration ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv ERROR[0m
[0mSubtask: Loading Configuration on the device (failed)
[0m
[0m[1m[32m---- Base Configuration ** changed : False ------------------------------------- INFO[0m
[0msystem {
  host-name leaf00.cmh;
  domain-name cmh.acme.local;
}[0m
[0m[1m[31m---- Loading Configuration on the device ** changed : False -------------------- ERROR[0m
[0mTraceback (most recent call last):
  File "/Users/dbarroso/.virtualenvs/nornir/lib/python3.6/site-packages/napalm/eos/eos.py", line 231, in _load_config
    self.device.run_commands(commands)
  File "/Users/dbarroso/.virtualenvs/nornir/lib/python3.6/site-packages/pyeapi/client.py", line 730, in run_commands
    response = self._connection.execute(commands[...]<jupyter_text>There is also a method that will raise an exception if the task had an error:<jupyter_code>from nornir.core.exceptions import NornirExecutionError
try:
    result.raise_on_error()
except NornirExecutionError:
    print("ERROR!!!")<jupyter_output>ERROR!!![0m
[0m<jupyter_text>Skipped hostsNornir will keep track of hosts that failed and won't run future tasks on them:<jupyter_code>from nornir.core.task import Result

def hi(task):
    return Result(host=task.host, result=f"{task.host.name}: Hi, I am still here!")
    
result = cmh.run(task=hi)
print_result(result)<jupyter_output>[1m[36mhi******************************************************************************[0m
[0m[1m[34m* leaf01.cmh ** changed : False ************************************************[0m
[0m[1m[32mvvvv hi ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO[0m
[0mleaf01.cmh: Hi, I am still here![0m
[0m[1m[32m^^^^ END hi ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^[0m
[0m[1m[34m* spine01.cmh ** changed : False ***********************************************[0m
[0m[1m[32mvvvv hi ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO[0m
[0mspine01.cmh: Hi, I am still here![0m
[0m[1m[32m^^^^ END hi ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^[0m
[0m<jupyter_text>You can force the execution of tasks on failed hosts by passing the arguments `run_on_failed=True`:<jupyter_code>result = cmh.run(task=hi, on_failed=True)
print_result(result)<jupyter_output>[1m[36mhi******************************************************************************[0m
[0m[1m[34m* leaf00.cmh ** changed : False ************************************************[0m
[0m[1m[32mvvvv hi ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO[0m
[0mleaf00.cmh: Hi, I am still here![0m
[0m[1m[32m^^^^ END hi ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^[0m
[0m[1m[34m* leaf01.cmh ** changed : False ************************************************[0m
[0m[1m[32mvvvv hi ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO[0m
[0mleaf01.cmh: Hi, I am still here![0m
[0m[1m[32m^^^^ END hi ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^[0m
[0m[1m[34m* spine00.cmh ** changed : False ***********************************************[0m
[0m[1m[32mvvvv hi ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO[0m
[0mspine00.cmh: Hi, I am[...]<jupyter_text>You can also exclude the hosts that are "good" if you want to with the `run_on_good` flag:<jupyter_code>result = cmh.run(task=hi, on_failed=True, on_good=False)
print_result(result)<jupyter_output>[1m[36mhi******************************************************************************[0m
[0m[1m[34m* leaf00.cmh ** changed : False ************************************************[0m
[0m[1m[32mvvvv hi ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO[0m
[0mleaf00.cmh: Hi, I am still here![0m
[0m[1m[32m^^^^ END hi ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^[0m
[0m[1m[34m* spine00.cmh ** changed : False ***********************************************[0m
[0m[1m[32mvvvv hi ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO[0m
[0mspine00.cmh: Hi, I am still here![0m
[0m[1m[32m^^^^ END hi ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^[0m
[0m<jupyter_text>To achieve this `nornir` keeps a list failed hosts in it's shared [data](../../ref/api/nornir.rstnornir.core.Data) object:<jupyter_code>nr.data.failed_hosts<jupyter_output><empty_output><jupyter_text>If you want to mark some hosts as succeeded and make them back eligible for future tasks you can do it individually per host with the function [recover_host](../../ref/api/nornir.rstnornir.core.Data.recover_host) or reset the list completely with [reset_failed_hosts](../../ref/api/nornir.rstnornir.core.Data.reset_failed_hosts):<jupyter_code>nr.data.reset_failed_hosts()
nr.data.failed_hosts<jupyter_output><empty_output><jupyter_text>Raise on error automaticallyAlternatively, you can configure nornir to raise the exception automatically in case of error with the `raise_on_error` configuration option:<jupyter_code>nr = InitNornir(config_file="config.yaml", raise_on_error=True)
cmh = nr.filter(site="cmh", type="network_device")
try:
    cmh.run(task=basic_configuration)
except NornirExecutionError:
    print("ERROR!!!")<jupyter_output>ERROR!!![0m
[0m
{'fraction_non_alphanumeric': 0.26332622601279315, 'fraction_numerical': 0.027452025586353946, 'mean_word_length': 6.866876310272537, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 8, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '12', 'dir': 'jupyter-structured-clean-dedup', 'id': '204063', 'license': 'Apache-2.0', 'n_tokens_mistral': 2710, 'n_tokens_neox': 2470, 'n_words': 505, 'path': 'docs/tutorials/intro/failed_tasks.ipynb', 'repo_name': 'omershtivi/nornir', 'size': '7504'}