任务

    任务是针对单台主机实现某种功能的一段可以重复使用的代码,例如收集信息等。

    在 nornir 中, 任务(Tasks) 是一个将 对象作为第一个参数并且返回值是 Result 对象的函数。

    在旧版本中,nornir 提供了一些内置的任务可以直接使用。从 3.0 版本开始,为了保持框架的纯粹性,剔除了除核心功能外的插件代码,现在需要自己来编写 Task 或者使用其他人贡献出来的插件。

    可以在 nornir.tech 中获取当前已经公开发布的插件。。

    1. # 初始化一个 nornir 对象
    2. # 导入 print_result 模块来处理 Result 对象
    3. from nornir import InitNornir
    4. from nornir_utils.plugins.functions import print_result
    5. nr = InitNornir(config_file="files/config.yaml")
    6. # 为了保持内容简洁,只针对一些主机进行操作
    7. nr = nr.filter(site='bj',role='spine')
    1. [2]:
    1. # 首先导入 Task 、Result 模块
    2. from nornir.core.task import Task, Result
    3. # 定义一个 task,作用是让主机输出 hello world。
    4. def hello_world(task: Task) -> Result:
    5. return Result(
    6. host=task.host,
    7. result=f"{task.host.name} says hello world!"
    8. )

    要运行这个 task,需要使用 nornir 对象的 run 方法,将 task 函数作为参数传递给 run,要获取到任务执行的结果,需要使用 print_result 方法打印出来:

    1. [3]:
    1. result = nr.run(task=hello_world)
    2. print_result(result)

    定义 Task 函数时,支持 **kwargs 来传参,这样可以扩展 task 的功能性,例如:

    1. [4]:
    1. def say(task: Task, text: str) -> Result:
    2. return Result(
    3. host=task.host,

    然后可以像之前一样使用 nornir 对象的 run 方法来运行 task,这次需要指定额外的参数 text

    1. [5]:
    1. result = nr.run(
    2. name="再见~",
    3. task=say,
    4. text="byebye!"
    5. )
    6. print_result(result)
    1. 再见~*****************************************************************************
    2. * spine00.bj ** changed : False ************************************************
    3. vvvv 再见~ ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
    4. spine00.bj says byebye!
    5. ^^^^ END 再见~ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    6. * spine01.bj ** changed : False ************************************************
    7. vvvv 再见~ ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
    8. spine01.bj says byebye!
    9. ^^^^ END 再见~ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    需要注意的是,在这个例子中传入了 name 参数来作为这个 task 的描述性名字,这个参数会在结果中显示出来。如果没有指定这个参数的话,则会使用 task 函数的名字。

    来定义一个新的 task:

    1. def count(task: Task, number: int) -> Result:
    2. return Result(
    3. host=task.host,
    4. result=f"{[n for n in range(0, number)]}"
    5. )

    然后将这个新的 task count 和之前的 say 结合起来,形成任务组,实现更复杂的工作流:

    1. [7]:
    1. def greet_and_count(task: Task, number: int) -> Result:
    2. task.run(
    3. name="你好~",
    4. text="Hi~",
    5. task.run(
    6. name="计数",
    7. task=count,
    8. number=number,
    9. )
    10. task.run(
    11. name="再见",
    12. task=say,
    13. text="byebye."
    14. )
    15. # 计算打招呼打了奇数次还是偶数次
    16. even_or_odds = "even" if number % 2 == 1 else "odd"
    17. return Result(
    18. host=task.host,
    19. result = f"{task.host} counted {even_or_odds} times!",
    20. )

    来简单分析一个这个 task: 1. 首先调用了 say 任务并传入了文本 “Hi~”; 2. 之后调用了 count 任务,它接收中在父任务 greet_and_count 也定义的参数 number,这样可以在执行父任务时动态调整这部分参数; 3. 然后再次调用了 say 任务,这次传入了文本 “byebye”; 4. 之后 if 来判断计数情况; 5. 最后返回了 Result 对象,将需要的信息返回。

    现在可以像调用一个普通的 task 一样来调用新定义的任务组:

    1. [8]:
    1. result = nr.run(
    2. name="对打招呼次数进行计数",
    3. task=greet_and_count,
    4. number=5,