Processors
Processors是可以在某些事件上执行代码的。
你可以把Processors当作一个处理任务结果的一个备选方法,不管怎么说他是有一些优点的。
因为它是基于事件的性质,你可以异步处理对应的事件。这意味着你可以直接处理某一个host的结果,不需要等所有的hosts全部执行完。
因为你可以利用事件的代码这样会更简洁更容易理解。
让我们通过一些例子看下processors是如何工作的。下面我们先载入nornir对象和一些我们需要的库:
from typing import Dict
from nornir import InitNornir
nr = InitNornir(config_file="config.yaml")
现在我们写一个processor可以把更多的任务处理信息打印出来:
#
from typing import Dict
from nornir.core import Nornir
from nornir.core.inventory import Host
from nornir.core.task import AggregatedResult, MultiResult, Result, Task
class PrintResult:
def task_started(self, task: Task) -> None:
print(f">>> starting: {task.name}")
def task_completed(self, task: Task, result: AggregatedResult) -> None:
print(f">>> completed: {task.name}")
def task_instance_started(self, task: Task, host: Host) -> None:
pass
def task_instance_completed(
self, task: Task, host: Host, result: MultiResult
) -> None:
print(f" - {host.name}: - {result.result}")
def subtask_instance_started(self, task: Task, host: Host) -> None:
pass # to keep example short and sweet we ignore subtasks
def subtask_instance_completed(
self, task: Task, host: Host, result: MultiResult
) -> None:
pass # to keep example short and sweet we ignore subtasks
现在我们写另一个processor将一些任务信息保存在一个字典中:
class SaveResultToDict:
def __init__(self, data: Dict[str, None]) -> None:
self.data = data
def task_started(self, task: Task) -> None:
self.data[task.name] = {}
self.data[task.name]["started"] = True
def task_completed(self, task: Task, result: AggregatedResult) -> None:
self.data[task.name]["completed"] = True
def task_instance_started(self, task: Task, host: Host) -> None:
self.data[task.name][host.name] = {"started": True}
def task_instance_completed(
self, task: Task, host: Host, result: MultiResult
) -> None:
self.data[task.name][host.name] = {
"completed": True,
"result": result.result,
}
def subtask_instance_started(self, task: Task, host: Host) -> None:
pass # 为了保持例子简洁和有趣我们忽略子任务
def subtask_instance_completed(
self, task: Task, host: Host, result: MultiResult
) -> None:
pass # 为了保持例子简洁和有趣我们忽略子任务
最后,为了测试processors我们用一个非常简单的任务,这个任务会代表每台设备给我们一个问候:
def greeter(task: Task, greet: str) -> Result:
return Result(host=task.host, result=f"{greet}! my name is {task.host.name}")
希望截至目前所有的事情已经结束,让我们把它应用起来:
data = {} # 这是一个字典SaveResultToDict将会在里面存储信息
# 和.filter和类似,with_processors返回nornir的对象副本,但是分配有processor。
# 让我们用这个方法来分配两个processors。
nr_with_processors = nr.with_processors([SaveResultToDict(data), PrintResult()])
# 现在我们用nr_with_processors来执行greeter任务
nr_with_processors.run(
name="hi!",
task=greeter,
greet="hi",
)
nr_with_processors.run(
name="bye!",
task=greeter,
greet="bye",
)
输出:
>>> starting: hi!
- host1.cmh: - hi! my name is host1.cmh
- host2.cmh: - hi! my name is host2.cmh
- spine00.cmh: - hi! my name is spine00.cmh
- spine01.cmh: - hi! my name is spine01.cmh
- leaf00.cmh: - hi! my name is leaf00.cmh
- leaf01.cmh: - hi! my name is leaf01.cmh
- host1.bma: - hi! my name is host1.bma
- host2.bma: - hi! my name is host2.bma
- spine00.bma: - hi! my name is spine00.bma
- spine01.bma: - hi! my name is spine01.bma
- leaf00.bma: - hi! my name is leaf00.bma - leaf01.bma: - hi! my name is leaf01.bma
>>> completed: hi!
>>> starting: bye!
- host1.cmh: - bye! my name is host1.cmh
- host2.cmh: - bye! my name is host2.cmh
- spine00.cmh: - bye! my name is spine00.cmh
- spine01.cmh: - bye! my name is spine01.cmh
- leaf00.cmh: - bye! my name is leaf00.cmh
- leaf01.cmh: - bye! my name is leaf01.cmh
- host1.bma: - bye! my name is host1.bma
- host2.bma: - bye! my name is host2.bma
- spine00.bma: - bye! my name is spine00.bma
- spine01.bma: - bye! my name is spine01.bma
- leaf00.bma: - bye! my name is leaf00.bma
- leaf01.bma: - bye! my name is leaf01.bma
>>> completed: bye!
print(result_bye)
输出:
AggregatedResult (bye!): {'host1.cmh': MultiResult: [Result: "bye!"], 'host2.cmh': MultiResult: [Result: "bye!"], 'spine00.cmh': MultiResult: [Result: "bye!"], 'spine01.cmh': MultiResult: [Result: "bye!"], 'leaf00.cmh': MultiResult: [Result: "bye!"], 'leaf01.cmh': MultiResult: [Result: "bye!"], 'host1.bma': MultiResult: [Result: "bye!"], 'host2.bma': MultiResult: [Result: "bye!"], 'spine00.bma': MultiResult: [Result: "bye!"], 'spine01.bma': MultiResult: [Result: "bye!"], 'leaf00.bma': MultiResult: [Result: "bye!"], 'leaf01.bma': MultiResult: [Result: "bye!"]}
首先你可能已经注意到了我们得到了所有的信息并且都被打印在了屏幕上。这是由我们的PrintResult处理器完成的。你可能也主要到了AggregatedResult的返回,我们甚至没有干扰它存储过程将它存储到一个变量中因为我们目前我们还不需要它。
现在我们看下是否SaveResultToDict已经将信心存到了data中:
import json
print(json.dumps(data, indent=4))
输出:
{
"hi!": {
"started": true,
"host1.cmh": {
"completed": true,
"result": "hi! my name is host1.cmh"
},
"host2.cmh": {
"completed": true,
"result": "hi! my name is host2.cmh"
},
"spine00.cmh": {
"completed": true,
"result": "hi! my name is spine00.cmh"
},
"spine01.cmh": {
"completed": true,
"result": "hi! my name is spine01.cmh"
},
"leaf00.cmh": {
"completed": true,
"result": "hi! my name is leaf00.cmh"
},
"leaf01.cmh": {
"completed": true,
"result": "hi! my name is leaf01.cmh"
},
"host1.bma": {
"completed": true,
"result": "hi! my name is host1.bma"
},
"host2.bma": {
"completed": true,
"result": "hi! my name is host2.bma"
},
"spine00.bma": {
"completed": true,
"result": "hi! my name is spine00.bma"
},
"spine01.bma": {
"completed": true,
"result": "hi! my name is spine01.bma"
},
"leaf00.bma": {
"completed": true,
"result": "hi! my name is leaf00.bma"
},
"leaf01.bma": {
"completed": true,
"result": "hi! my name is leaf01.bma"
},
"completed": true
},
"bye!": {
"started": true,
"host1.cmh": {
"completed": true,
"result": "bye! my name is host1.cmh"
},
"host2.cmh": {
"completed": true,
"result": "bye! my name is host2.cmh"
},
"spine00.cmh": {
"completed": true,
"result": "bye! my name is spine00.cmh"
},
"spine01.cmh": {
"completed": true,
"result": "bye! my name is spine01.cmh"
},
"leaf00.cmh": {
"completed": true,
"result": "bye! my name is leaf00.cmh"
},
"leaf01.cmh": {
"completed": true,
"result": "bye! my name is leaf01.cmh"
},
"host1.bma": {
"completed": true,
"result": "bye! my name is host1.bma"
},
"host2.bma": {
"completed": true,
"result": "bye! my name is host2.bma"
},
"spine00.bma": {
"completed": true,
"result": "bye! my name is spine00.bma"
},
"spine01.bma": {
"completed": true,
"result": "bye! my name is spine01.bma"
},
"leaf00.bma": {
"completed": true,
"result": "bye! my name is leaf00.bma"
},
"leaf01.bma": {
"completed": true,
"result": "bye! my name is leaf01.bma"
},
"completed": true
}
}
如你所见通过processors去执行多个动作是非常简单的。你还是可以通过其他方法得到结果的,但是由于Processor的存在你可以能不需要他们了。
Idea
通过Processors还可以做什么:
- 发送事件提醒给即时通信软件或者日志系统。
- 可以让用户实时看到程序的执行情况,不用等到所有的hosts都执行完(尤其是有很多hosts的时候)。
- 通知某人或者提出一个告警当一个任务失败时。
- 等等...