任务

之前的内容中已经了解了如何初始化 nornir 对象并查看其主机清单和主机组信息,这节内容说明了如何在主机或主机组中执行任务。

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

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

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

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

现在来看一个 Task 的示例:

  1. [1]:
  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)
  1. hello_world*********************************************************************
  2. * spine00.bj ** changed : False ************************************************
  3. vvvv hello_world ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
  4. spine00.bj says hello world!
  5. ^^^^ END hello_world ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  6. * spine01.bj ** changed : False ************************************************
  7. vvvv hello_world ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
  8. spine01.bj says hello world!
  9. ^^^^ END hello_world ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

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

  1. [4]:
  1. def say(task: Task, text: str) -> Result:
  2. return Result(
  3. host=task.host,
  4. result=f"{task.host.name} says {text}"
  5. )

然后可以像之前一样使用 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 函数的名字。

任务组

一个任务(Tasks)可以调用其他的任务,这样就可以使用多个功能来组成更复杂的功能,这称为任务组(Grouping tasks)。

来定义一个新的 task:

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

来简单分析一个这个 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,
  5. )
  6. print_result(result)
  1. 对打招呼次数进行计数**********************************************************************
  2. * spine00.bj ** changed : False ************************************************
  3. vvvv 对打招呼次数进行计数 ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
  4. spine00.bj counted even times!
  5. ---- 你好~ ** changed : False ---------------------------------------------------- INFO
  6. spine00.bj says Hi~
  7. ---- 计数 ** changed : False ----------------------------------------------------- INFO
  8. [0, 1, 2, 3, 4]
  9. ---- 再见 ** changed : False ----------------------------------------------------- INFO
  10. spine00.bj says byebye.
  11. ^^^^ END 对打招呼次数进行计数 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  12. * spine01.bj ** changed : False ************************************************
  13. vvvv 对打招呼次数进行计数 ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
  14. spine01.bj counted even times!
  15. ---- 你好~ ** changed : False ---------------------------------------------------- INFO
  16. spine01.bj says Hi~
  17. ---- 计数 ** changed : False ----------------------------------------------------- INFO
  18. [0, 1, 2, 3, 4]
  19. ---- 再见 ** changed : False ----------------------------------------------------- INFO
  20. spine01.bj says byebye.
  21. ^^^^ END 对打招呼次数进行计数 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^