Trying to understand nornir_utils.plugins.tasks.files.write_file

Apologies if this is a stupid question, I’ve tried to do my due diligence by searching around for examples, reading the documentation, and looking at the code, but I’m still stuck.

I’m trying to export the results of some netmiko_send_commands to a text file.

I init Nornir and then create the following “results” variable:

results = nr.run(task=netmiko_send_command, command_string=cmds)

I then take this “results” variable (which contains the AggregatedResult object) and I pass it to another function “output_results(result)”

I first print the results to my screen using nornir_utils.plugins.functions.print_result

This works fine and the AggregatedResult object is printed to my screen with all the content that resulted from running those netmiko commands.

However, I can’t seem to get write_file function to work.

I’m trying to run it like this:
write_file(task=Task, content=result, filename=‘results.txt’)

I don’t understand what should be going in the “task” value. In the write_file code I see

from nornir.core.task import Result, Task

and

def write_file(
task: Task,
filename: str,
content: str,
append: bool = False,
dry_run: Optional[bool] = None,

) -> Result:

Earlier in my code I InitNornir as nr and run a task like this:

results = nr.run(task=netmiko_send_command, command_string=cmds)

But because I’m only Initing Nornir within that function, I can’t run write_file the same way later in my code.

Is there a specific way I’m supposed to be able to export the results of netmiko_send_command to a text file?

Could you just use write_file in the same function as the nr object? You also need to work out how you get the results out of the AggregatedResult object to print.

Are you wanting to just write to file the same or similar output to print_results? There are many ways of approaching this. To give you some ideas, a custom print_result or tee stdout. Most people would probably go for a custom print_result :slight_smile:

from pprint import pformat

content = ""
for device_name, multi_result in results.items():
  content += "="*30 + f" {device_name} " + "="*30 + "\n"
  for result in multi_result:
    if not isinstance(result.result, type(None)):
      content += result.name + "\n"
      content += pformat(result.result) + "\n"

with open("results.txt","w") as f:
  f.write(content)

Or, for something totally different, try tee’ing stdout for print_results to a file

import sys
import re
#Tee StdOut to File
class TeeStdOut(object):
    def __init__(self, name, mode):
        self.file = open(name, mode)
        self.stdout = sys.stdout
        sys.stdout = self
        #some regex for removing formatting
        self.ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
    def close(self):
        if self is None:
            return
        if self.stdout is not None:
            sys.stdout = self.stdout
            self.stdout = None
        if self.file is not None:
            self.file.close()
            self.file = None
    def write(self, data):
        #remove some formatting for text file
        file_data = self.ansi_escape.sub('', data)
        self.file.write(file_data)
        self.stdout.write(data)
    def flush(self):
        self.file.flush()
        self.stdout.flush()
    def __del__(self):
        self.close()
    def __enter__(self):
      return self
    def __exit__(self, type, value, traceback):
      #Exception handling here
      self.close()

with TeeStdOut('results.txt', 'w') as stdout_tee:
  print_result(results)

Here is an advanced example where I am running multiple netmiko_send_commands and saving as a json file. It also contains an extra function “add_extras”, which I could then use in other scripts to import the previously data gathered for extra filtering, sanity checks, building stuff with, etc.

https://raw.githubusercontent.com/no-such-anthony/nornir3_play/master/get_extras.py

Some other considerations to your questions,

  • Maybe have a custom task which would run both the netmiko_send_commands and write_file. That way you can have one result file per device.

This simple example works for a single netmiko_send_command within a custom task function

task.run(write_file,
         filename=f'{RESULTDIR}/{task.host}.cfg',
         content=result.result)
  • There are ways to hack up enough of a dummy Task-like object that would allow write_file to work elsewhere, but you would be better off just creating your own alternate write_file if you decided to head in that direction.