From 96901416d5907147be823f667c38372084f77b99 Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Fri, 8 Dec 2017 14:27:46 +0100 Subject: [PATCH 01/67] Store traceback instead of exc --- brigade/core/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brigade/core/__init__.py b/brigade/core/__init__.py index 7cf5af0a..705c1981 100644 --- a/brigade/core/__init__.py +++ b/brigade/core/__init__.py @@ -106,7 +106,7 @@ def _run_parallel(self, task, num_workers, **kwargs): host, res, exc, traceback = r.get() if exc: result.failed_hosts[host] = exc - result.tracebacks[host] = exc + result.tracebacks[host] = traceback else: result[host] = res return result From 4a82462828e0115fb0b059df438a47918316be42 Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Sat, 9 Dec 2017 10:32:06 +0100 Subject: [PATCH 02/67] Remove installation of sshpass --- .travis.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7bbf31f5..8952416b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,11 +3,6 @@ sudo: required services: - docker -addons: - apt: - packages: - - sshpass - language: python python: - 2.7 From 7b29f77757b53ae1be7670ab08f397906f46f434 Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Sat, 9 Dec 2017 15:07:23 +0100 Subject: [PATCH 03/67] WIP: Tutorial --- docs/tutorials/intro/index.rst | 6 ++++++ docs/tutorials/intro/overview.rst | 17 +++++++++++++++++ docs/tutorials/intro/python.rst | 4 ++++ 3 files changed, 27 insertions(+) create mode 100644 docs/tutorials/intro/python.rst diff --git a/docs/tutorials/intro/index.rst b/docs/tutorials/intro/index.rst index 2c70105f..68737c6c 100644 --- a/docs/tutorials/intro/index.rst +++ b/docs/tutorials/intro/index.rst @@ -1,11 +1,17 @@ Learning Brigade ================ +We're glad you made it here! This is a great place to learn the basics of Brigade. Good luck on your journey. + +.. note:: + + Just like Brigade, this tutorial and the rest of the documentation is a work in progress. If you are missing something, please open `an issue `_. .. toctree:: :maxdepth: 1 Brigade at a glance + 100% Python Installation guide Creating an inventory Exploring the inventory diff --git a/docs/tutorials/intro/overview.rst b/docs/tutorials/intro/overview.rst index 84eb542a..a32a478e 100644 --- a/docs/tutorials/intro/overview.rst +++ b/docs/tutorials/intro/overview.rst @@ -1,3 +1,20 @@ What is Brigade? ================ +Brigade is an automation framework written in Python. These days there exists several automation frameworks. What makes Brigade different is that you write Python code in order to use Brigade. This is to be compared to other frameworks which typically use their own configuration language. + +Why does this matter? +--------------------- +Typically, a specific configuration language is easy to use in a basic way. Though after a while you need to use more advanced features and might have to extend that configuration language with another programming language. While this works it can be very hard to troubleshoot once it's started to grow. + +As Brigade allows you to use pure Python code you can troubleshoot and debug it in the same way as you would do with any other Python code. + +What does it compare to? +------------------------ +In some ways, you could compare Brigade to `Flask `_, which is a web framework that allows you to create web applications. Flask provides an easy to use interface which lets you build powerful websites without forcing you to work in a particular way. + +Brigade lets you automate your environment by providing you an interface which does a lot of the heavy lifting. + +How much Python do you need do know? +------------------------------------ +As you write Python code to control Brigade it's assumed that you are somewhat familiar with Python. But how good do you have to be with Python in order to make use of Brigade? That's actually the topic for the next section *spoiler alert* Not a lot! \ No newline at end of file diff --git a/docs/tutorials/intro/python.rst b/docs/tutorials/intro/python.rst new file mode 100644 index 00000000..e92476b8 --- /dev/null +++ b/docs/tutorials/intro/python.rst @@ -0,0 +1,4 @@ +The need to know Python +======================= + + From 80d60bfd38831d44d696ee4bae19cae38e0f140a Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Sat, 9 Dec 2017 16:55:58 +0100 Subject: [PATCH 04/67] Add Python text --- docs/tutorials/intro/python.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/tutorials/intro/python.rst b/docs/tutorials/intro/python.rst index e92476b8..3b7be274 100644 --- a/docs/tutorials/intro/python.rst +++ b/docs/tutorials/intro/python.rst @@ -1,4 +1,27 @@ The need to know Python ======================= +In order to use Brigade you have to know some Python. This might come as wonderful news to you, or you might find it a bit scary. If you're already a comfortable Python user, just go a head and hit :doc:`next `. +If you haven't written any code before you might be heading somewhere else now. Before you go however, answer me this: + +Do you know Excel? + +Chances are that you know how to use Excel. It's simple right. You just open a sheet and enter some data. It's used by a lot of finance people and unfortunately it's one of the most used IPAM solutions. Though aside from a simple tool to enter data in a sheet Excel has an insane amount of features. Most people will only use 5% of all the features. How good are you at Excel? Does it matter? + +It's the same way with Python, it can take very long time to fully master it. The good part is that you don't have to become a master. As long as you know the very basics you will be able to use Brigade. + +Python skills required +---------------------- + +If you've never seen Python before, and don't have any experience in other programming languages it will probably be a good idea to `pick up the basics `_ and come back here later. + +In order to follow this tutorial you should be able to: + +* Setup Python on your system (Linux or Mac) +* Install Virtualenv and Python packages +* Understand basic Python concepts such as: + + - Variables + - Functions + - Imports From c03f75dab19a39c0c72b88eb30f4dfbb43da5e5c Mon Sep 17 00:00:00 2001 From: David Barroso Date: Sun, 10 Dec 2017 14:22:28 +0100 Subject: [PATCH 05/67] connections as tasks --- brigade/core/inventory.py | 59 +++++-------------- .../plugins/tasks/commands/remote_command.py | 5 +- brigade/plugins/tasks/connections/__init__.py | 5 ++ .../tasks/connections/paramiko_connection.py | 48 +++++++++++++++ brigade/plugins/tasks/files/sftp.py | 5 +- 5 files changed, 75 insertions(+), 47 deletions(-) create mode 100644 brigade/plugins/tasks/connections/__init__.py create mode 100644 brigade/plugins/tasks/connections/paramiko_connection.py diff --git a/brigade/core/inventory.py b/brigade/core/inventory.py index 41603d86..b99997a5 100644 --- a/brigade/core/inventory.py +++ b/brigade/core/inventory.py @@ -1,9 +1,7 @@ import getpass -import os from brigade.core import helpers - -import paramiko +from brigade.plugins.tasks import connections class Host(object): @@ -171,48 +169,19 @@ def nos(self): """Network OS the device is running. Defaults to ``brigade_nos``.""" return self.get("brigade_nos") - @property - def ssh_connection(self): - """Reusable :obj:`paramiko.client.SSHClient`.""" - if hasattr(self, "_ssh_connection"): - return self._ssh_connection - - # TODO configurable - ssh_config_file = os.path.join(os.path.expanduser("~"), ".ssh", "config") - - client = paramiko.SSHClient() - client._policy = paramiko.WarningPolicy() - client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - ssh_config = paramiko.SSHConfig() - if os.path.exists(ssh_config_file): - with open(ssh_config_file) as f: - ssh_config.parse(f) - - parameters = { - "hostname": self.host, - "username": self.username, - "password": self.password, - "port": self.ssh_port, - } - - user_config = ssh_config.lookup(self.host) - for k in ('hostname', 'username', 'port'): - if k in user_config: - parameters[k] = user_config[k] - - if 'proxycommand' in user_config: - parameters['sock'] = paramiko.ProxyCommand(user_config['proxycommand']) - - # TODO configurable - # if ssh_key_file: - # parameters['key_filename'] = ssh_key_file - if 'identityfile' in user_config: - parameters['key_filename'] = user_config['identityfile'] - - client.connect(**parameters) - self._ssh_connection = client - return client + def get_connection(self, connection_name): + """ + This function will try to find an already established connection + or call the task that establishes the connection if none is found. + + Arguments: + connection_name (str): Name of the connection, for instance, netmiko, paramiko, + napalm... + """ + attr_name = "{}_connection".format(connection_name) + if not hasattr(self, attr_name): + getattr(connections, attr_name)(host=self) + return getattr(self, attr_name) class Group(Host): diff --git a/brigade/plugins/tasks/commands/remote_command.py b/brigade/plugins/tasks/commands/remote_command.py index 7b241e70..67620167 100644 --- a/brigade/plugins/tasks/commands/remote_command.py +++ b/brigade/plugins/tasks/commands/remote_command.py @@ -11,6 +11,9 @@ def remote_command(task, command): """ Executes a command locally + Requires: + paramiko_connection + Arguments: command (``str``): command to execute @@ -22,7 +25,7 @@ def remote_command(task, command): Raises: :obj:`brigade.core.exceptions.CommandError`: when there is a command error """ - client = task.host.ssh_connection + client = task.host.get_connection("paramiko") chan = client.get_transport().open_session() chan.exec_command(command) diff --git a/brigade/plugins/tasks/connections/__init__.py b/brigade/plugins/tasks/connections/__init__.py new file mode 100644 index 00000000..1f8c079b --- /dev/null +++ b/brigade/plugins/tasks/connections/__init__.py @@ -0,0 +1,5 @@ +from .paramiko_connection import paramiko_connection + +__all__ = ( + "paramiko_connection", +) diff --git a/brigade/plugins/tasks/connections/paramiko_connection.py b/brigade/plugins/tasks/connections/paramiko_connection.py new file mode 100644 index 00000000..3c64c197 --- /dev/null +++ b/brigade/plugins/tasks/connections/paramiko_connection.py @@ -0,0 +1,48 @@ +import os + +import paramiko + + +def paramiko_connection(task=None, host=None): + """ + This tasks connects with paramiko to the device and sets the + attribute ``paramiko_connection``. + """ + if host is None: + host = task.host + + # TODO configurable + ssh_config_file = os.path.join(os.path.expanduser("~"), ".ssh", "config") + + client = paramiko.SSHClient() + client._policy = paramiko.WarningPolicy() + client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + ssh_config = paramiko.SSHConfig() + if os.path.exists(ssh_config_file): + with open(ssh_config_file) as f: + ssh_config.parse(f) + + parameters = { + "hostname": host.host, + "username": host.username, + "password": host.password, + "port": host.ssh_port, + } + + user_config = ssh_config.lookup(host.host) + for k in ('hostname', 'username', 'port'): + if k in user_config: + parameters[k] = user_config[k] + + if 'proxycommand' in user_config: + parameters['sock'] = paramiko.ProxyCommand(user_config['proxycommand']) + + # TODO configurable + # if ssh_key_file: + # parameters['key_filename'] = ssh_key_file + if 'identityfile' in user_config: + parameters['key_filename'] = user_config['identityfile'] + + client.connect(**parameters) + host.paramiko_connection = client diff --git a/brigade/plugins/tasks/files/sftp.py b/brigade/plugins/tasks/files/sftp.py index d32608a7..b286b61d 100644 --- a/brigade/plugins/tasks/files/sftp.py +++ b/brigade/plugins/tasks/files/sftp.py @@ -107,6 +107,9 @@ def sftp(task, src, dst, action): """ Transfer files from/to the device using sftp protocol + Requires: + paramiko_connection + Example:: brigade.run(files.sftp, @@ -130,7 +133,7 @@ def sftp(task, src, dst, action): "put": put, "get": get, } - client = task.host.ssh_connection + client = task.host.get_connection("paramiko") scp_client = SCPClient(client.get_transport()) sftp_client = paramiko.SFTPClient.from_transport(client.get_transport()) files_changed = actions[action](task, scp_client, sftp_client, src, dst) From d2e199d1544ac19bd733494889df764c6bad2cd0 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Sun, 10 Dec 2017 19:11:14 +0100 Subject: [PATCH 06/67] use a dictionary to store connections --- brigade/core/inventory.py | 11 ++++++----- .../plugins/tasks/connections/paramiko_connection.py | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/brigade/core/inventory.py b/brigade/core/inventory.py index b99997a5..b910a7c6 100644 --- a/brigade/core/inventory.py +++ b/brigade/core/inventory.py @@ -61,6 +61,7 @@ def __init__(self, name, group=None, **kwargs): self.group = group self.data = {} self.data["name"] = name + self.connections = {} if isinstance(group, str): self.data["group"] = group @@ -169,7 +170,7 @@ def nos(self): """Network OS the device is running. Defaults to ``brigade_nos``.""" return self.get("brigade_nos") - def get_connection(self, connection_name): + def get_connection(self, connection): """ This function will try to find an already established connection or call the task that establishes the connection if none is found. @@ -178,10 +179,10 @@ def get_connection(self, connection_name): connection_name (str): Name of the connection, for instance, netmiko, paramiko, napalm... """ - attr_name = "{}_connection".format(connection_name) - if not hasattr(self, attr_name): - getattr(connections, attr_name)(host=self) - return getattr(self, attr_name) + if connection not in self.connections: + task_name = "{}_connection".format(connection) + getattr(connections, task_name)(host=self) + return self.connections[connection] class Group(Host): diff --git a/brigade/plugins/tasks/connections/paramiko_connection.py b/brigade/plugins/tasks/connections/paramiko_connection.py index 3c64c197..ab0081a8 100644 --- a/brigade/plugins/tasks/connections/paramiko_connection.py +++ b/brigade/plugins/tasks/connections/paramiko_connection.py @@ -45,4 +45,4 @@ def paramiko_connection(task=None, host=None): parameters['key_filename'] = user_config['identityfile'] client.connect(**parameters) - host.paramiko_connection = client + host.connections["paramiko"] = client From 4fa7e6dee10a884a997299d108e8d026fed66998 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Sun, 10 Dec 2017 22:10:52 +0100 Subject: [PATCH 07/67] reuse napalm connection --- brigade/core/inventory.py | 18 +++++++- brigade/plugins/tasks/connections/__init__.py | 2 + .../tasks/connections/napalm_connection.py | 28 +++++++++++++ .../tasks/connections/paramiko_connection.py | 4 +- .../plugins/tasks/networking/napalm_cli.py | 25 ++--------- .../tasks/networking/napalm_configure.py | 41 ++++++------------- .../plugins/tasks/networking/napalm_get.py | 34 ++++----------- .../step1/discard_config.1 | 1 - tests/tasks/networking/test_napalm_cli.py | 11 ++--- .../tasks/networking/test_napalm_configure.py | 28 ++++++------- tests/tasks/networking/test_napalm_get.py | 20 +++++---- 11 files changed, 103 insertions(+), 109 deletions(-) create mode 100644 brigade/plugins/tasks/connections/napalm_connection.py delete mode 100644 tests/tasks/networking/mocked/napalm_configure/test_napalm_configure_change_commit/step1/discard_config.1 diff --git a/brigade/core/inventory.py b/brigade/core/inventory.py index b910a7c6..c1210f9c 100644 --- a/brigade/core/inventory.py +++ b/brigade/core/inventory.py @@ -1,9 +1,13 @@ import getpass +import logging from brigade.core import helpers from brigade.plugins.tasks import connections +logger = logging.getLogger("brigade") + + class Host(object): """ Represents a host. @@ -174,6 +178,13 @@ def get_connection(self, connection): """ This function will try to find an already established connection or call the task that establishes the connection if none is found. + In any case, it should always return an established connection or + an error if the connection is not already established and we don't + know of any task that could provide that type of connection. + + Raises: + AttributeError: if it's unknown how to establish a connection for the given + type Arguments: connection_name (str): Name of the connection, for instance, netmiko, paramiko, @@ -181,7 +192,12 @@ def get_connection(self, connection): """ if connection not in self.connections: task_name = "{}_connection".format(connection) - getattr(connections, task_name)(host=self) + try: + task = getattr(connections, task_name) + except AttributeError: + raise AttributeError("not sure how to establish a connection for {}".format( + connection)) + task(host=self) return self.connections[connection] diff --git a/brigade/plugins/tasks/connections/__init__.py b/brigade/plugins/tasks/connections/__init__.py index 1f8c079b..4a93caeb 100644 --- a/brigade/plugins/tasks/connections/__init__.py +++ b/brigade/plugins/tasks/connections/__init__.py @@ -1,5 +1,7 @@ +from .napalm_connection import napalm_connection from .paramiko_connection import paramiko_connection __all__ = ( + "napalm_connection", "paramiko_connection", ) diff --git a/brigade/plugins/tasks/connections/napalm_connection.py b/brigade/plugins/tasks/connections/napalm_connection.py new file mode 100644 index 00000000..7cd17f79 --- /dev/null +++ b/brigade/plugins/tasks/connections/napalm_connection.py @@ -0,0 +1,28 @@ +from napalm import get_network_driver + + +def napalm_connection(task=None, host=None, timeout=60, optional_args=None): + """ + This tasks connects to the device with paramiko to the device and sets the + relevant connection. + + Arguments: + timeout (int, optional): defaults to 60 + optional_args (dict, optional): defaults to ``{"port": task.host["napalm_port"]}`` + """ + if host is None: + host = task.host + + parameters = { + "hostname": task.host.host, + "username": task.host.username, + "password": task.host.password, + "timeout": timeout, + "optional_args": optional_args or {}, + } + if "port" not in parameters["optional_args"] and task.host.network_api_port: + parameters["optional_args"]["port"] = task.host.network_api_port + network_driver = get_network_driver(task.host.nos) + + host.connections["napalm"] = network_driver(**parameters) + host.connections["napalm"].open() diff --git a/brigade/plugins/tasks/connections/paramiko_connection.py b/brigade/plugins/tasks/connections/paramiko_connection.py index ab0081a8..81836997 100644 --- a/brigade/plugins/tasks/connections/paramiko_connection.py +++ b/brigade/plugins/tasks/connections/paramiko_connection.py @@ -5,8 +5,8 @@ def paramiko_connection(task=None, host=None): """ - This tasks connects with paramiko to the device and sets the - attribute ``paramiko_connection``. + This tasks connects to the device with paramiko to the device and sets the + relevant connection. """ if host is None: host = task.host diff --git a/brigade/plugins/tasks/networking/napalm_cli.py b/brigade/plugins/tasks/networking/napalm_cli.py index 5e805627..59879ac5 100644 --- a/brigade/plugins/tasks/networking/napalm_cli.py +++ b/brigade/plugins/tasks/networking/napalm_cli.py @@ -1,33 +1,14 @@ from brigade.core.task import Result -from napalm import get_network_driver - -def napalm_cli(task, commands, timeout=60, optional_args=None): +def napalm_cli(task, commands): """ Run commands on remote devices using napalm - Arguments: - commands (list): list of commands to execute on the device - timeout (int, optional): defaults to 60 - optional_args (dict, optional): defaults to ``{"port": task.host["napalm_port"]}`` - - Returns: :obj:`brigade.core.task.Result`: * result (``dict``): dictionary with the result of the commands """ - parameters = { - "hostname": task.host.host, - "username": task.host.username, - "password": task.host.password, - "timeout": timeout, - "optional_args": optional_args or {}, - } - if "port" not in parameters["optional_args"] and task.host.network_api_port: - parameters["optional_args"]["port"] = task.host.network_api_port - network_driver = get_network_driver(task.host.nos) - - with network_driver(**parameters) as device: - result = device.cli(commands) + device = task.host.get_connection("napalm") + result = device.cli(commands) return Result(host=task.host, result=result) diff --git a/brigade/plugins/tasks/networking/napalm_configure.py b/brigade/plugins/tasks/networking/napalm_configure.py index a2f98cd1..895d06b4 100644 --- a/brigade/plugins/tasks/networking/napalm_configure.py +++ b/brigade/plugins/tasks/networking/napalm_configure.py @@ -1,44 +1,29 @@ from brigade.core.task import Result -from napalm import get_network_driver - -def napalm_configure(task, configuration, replace=False, timeout=60, optional_args=None): +def napalm_configure(task, configuration, replace=False): """ Loads configuration into a network devices using napalm Arguments: configuration (str): configuration to load into the device replace (bool): whether to replace or merge the configuration - timeout (int, optional): defaults to 60 - optional_args (dict, optional): defaults to ``{"port": task.host["napalm_port"]}`` - Returns: :obj:`brigade.core.task.Result`: * changed (``bool``): whether if the task is changing the system or not * diff (``string``): change in the system """ - parameters = { - "hostname": task.host.host, - "username": task.host.username, - "password": task.host.password, - "timeout": timeout, - "optional_args": optional_args or {}, - } - if "port" not in parameters["optional_args"] and task.host.network_api_port: - parameters["optional_args"]["port"] = task.host.network_api_port - network_driver = get_network_driver(task.host.nos) - - with network_driver(**parameters) as device: - if replace: - device.load_replace_candidate(config=configuration) - else: - device.load_merge_candidate(config=configuration) - diff = device.compare_config() - - if task.dry_run: - device.discard_config() - else: - device.commit_config() + device = task.host.get_connection("napalm") + + if replace: + device.load_replace_candidate(config=configuration) + else: + device.load_merge_candidate(config=configuration) + diff = device.compare_config() + + if task.dry_run: + device.discard_config() + else: + device.commit_config() return Result(host=task.host, diff=diff, changed=len(diff) > 0) diff --git a/brigade/plugins/tasks/networking/napalm_get.py b/brigade/plugins/tasks/networking/napalm_get.py index e63c5752..ef4ff065 100644 --- a/brigade/plugins/tasks/networking/napalm_get.py +++ b/brigade/plugins/tasks/networking/napalm_get.py @@ -1,45 +1,27 @@ from brigade.core.task import Result -from napalm import get_network_driver - -def napalm_get(task, getters, timeout=60, optional_args=None): +def napalm_get(task, getters): """ Gather information from network devices using napalm Arguments: getters (list of str): getters to use - hostname (string, optional): defaults to ``brigade_ip`` - username (string, optional): defaults to ``brigade_username`` - password (string, optional): defaults to ``brigade_password`` - driver (string, optional): defaults to ``nos`` - timeout (int, optional): defaults to 60 - optional_args (dict, optional): defaults to ``{"port": task.host["napalm_port"]}`` Returns: :obj:`brigade.core.task.Result`: * result (``dict``): dictionary with the result of the getter """ - parameters = { - "hostname": task.host.host, - "username": task.host.username, - "password": task.host.password, - "timeout": timeout, - "optional_args": optional_args or {}, - } - if "port" not in parameters["optional_args"] and task.host.network_api_port: - parameters["optional_args"]["port"] = task.host.network_api_port - network_driver = get_network_driver(task.host.nos) + device = task.host.get_connection("napalm") if not isinstance(getters, list): getters = [getters] - with network_driver(**parameters) as device: - result = {} - for g in getters: - if not g.startswith("get_"): - getter = "get_{}".format(g) - method = getattr(device, getter) - result[g] = method() + result = {} + for g in getters: + if not g.startswith("get_"): + getter = "get_{}".format(g) + method = getattr(device, getter) + result[g] = method() return Result(host=task.host, result=result) diff --git a/tests/tasks/networking/mocked/napalm_configure/test_napalm_configure_change_commit/step1/discard_config.1 b/tests/tasks/networking/mocked/napalm_configure/test_napalm_configure_change_commit/step1/discard_config.1 deleted file mode 100644 index 0967ef42..00000000 --- a/tests/tasks/networking/mocked/napalm_configure/test_napalm_configure_change_commit/step1/discard_config.1 +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/tests/tasks/networking/test_napalm_cli.py b/tests/tasks/networking/test_napalm_cli.py index b3bc0a90..4eec82b5 100644 --- a/tests/tasks/networking/test_napalm_cli.py +++ b/tests/tasks/networking/test_napalm_cli.py @@ -1,7 +1,7 @@ import os # from brigade.core.exceptions import BrigadeExecutionError -from brigade.plugins.tasks import networking +from brigade.plugins.tasks import connections, networking # from napalm.base import exceptions @@ -15,10 +15,11 @@ class Test(object): def test_napalm_cli(self, brigade): opt = {"path": THIS_DIR + "/test_napalm_cli"} - result = brigade.filter(name="dev3.group_2").run(networking.napalm_cli, - commands=["show version", - "show interfaces"], - optional_args=opt) + d = brigade.filter(name="dev3.group_2") + d.run(connections.napalm_connection, optional_args=opt) + result = d.run(networking.napalm_cli, + commands=["show version", + "show interfaces"]) assert result for h, r in result.items(): assert r.result["show version"] diff --git a/tests/tasks/networking/test_napalm_configure.py b/tests/tasks/networking/test_napalm_configure.py index 2335fc1e..2208bdd5 100644 --- a/tests/tasks/networking/test_napalm_configure.py +++ b/tests/tasks/networking/test_napalm_configure.py @@ -1,7 +1,7 @@ import os from brigade.core.exceptions import BrigadeExecutionError -from brigade.plugins.tasks import networking +from brigade.plugins.tasks import connections, networking from napalm.base import exceptions @@ -16,9 +16,9 @@ class Test(object): def test_napalm_configure_change_dry_run(self, brigade): opt = {"path": THIS_DIR + "/test_napalm_configure_change_dry_run"} configuration = "hostname changed-hostname" - result = brigade.filter(name="dev3.group_2").run(networking.napalm_configure, - configuration=configuration, - optional_args=opt) + d = brigade.filter(name="dev3.group_2") + d.run(connections.napalm_connection, optional_args=opt) + result = d.run(networking.napalm_configure, configuration=configuration) assert result for h, r in result.items(): assert "+hostname changed-hostname" in r.diff @@ -28,19 +28,17 @@ def test_napalm_configure_change_commit(self, brigade): opt = {"path": THIS_DIR + "/test_napalm_configure_change_commit/step1"} configuration = "hostname changed-hostname" brigade.dry_run = False - result = brigade.filter(name="dev3.group_2").run(networking.napalm_configure, - num_workers=1, - configuration=configuration, - optional_args=opt) - brigade.dry_run = True + d = brigade.filter(name="dev3.group_2") + d.run(connections.napalm_connection, optional_args=opt) + result = d.run(networking.napalm_configure, configuration=configuration) + d.dry_run = True assert result for h, r in result.items(): assert "+hostname changed-hostname" in r.diff assert r.changed opt = {"path": THIS_DIR + "/test_napalm_configure_change_commit/step2"} - result = brigade.filter(name="dev3.group_2").run(networking.napalm_configure, - configuration=configuration, - optional_args=opt) + d.run(connections.napalm_connection, optional_args=opt) + result = d.run(networking.napalm_configure, configuration=configuration) assert result for h, r in result.items(): assert "+hostname changed-hostname" not in r.diff @@ -50,10 +48,10 @@ def test_napalm_configure_change_error(self, brigade): opt = {"path": THIS_DIR + "/test_napalm_configure_change_error"} configuration = "hostname changed_hostname" + d = brigade.filter(name="dev3.group_2") + d.run(connections.napalm_connection, optional_args=opt) with pytest.raises(BrigadeExecutionError) as e: - brigade.filter(name="dev3.group_2").run(networking.napalm_configure, - configuration=configuration, - optional_args=opt) + d.run(networking.napalm_configure, configuration=configuration) assert len(e.value.failed_hosts) for exc in e.value.failed_hosts.values(): assert isinstance(exc, exceptions.MergeConfigException) diff --git a/tests/tasks/networking/test_napalm_get.py b/tests/tasks/networking/test_napalm_get.py index 000b6f37..d3520bb1 100644 --- a/tests/tasks/networking/test_napalm_get.py +++ b/tests/tasks/networking/test_napalm_get.py @@ -1,7 +1,7 @@ import os from brigade.core.exceptions import BrigadeExecutionError -from brigade.plugins.tasks import networking +from brigade.plugins.tasks import connections, networking import pytest @@ -13,10 +13,11 @@ class Test(object): def test_napalm_getters(self, brigade): opt = {"path": THIS_DIR + "/test_napalm_getters"} - result = brigade.filter(name="dev3.group_2").run(networking.napalm_get, - getters=["facts", - "interfaces"], - optional_args=opt) + d = brigade.filter(name="dev3.group_2") + d.run(connections.napalm_connection, optional_args=opt) + result = d.run(networking.napalm_get, + getters=["facts", + "interfaces"]) assert result for h, r in result.items(): assert r.result["facts"] @@ -24,12 +25,13 @@ def test_napalm_getters(self, brigade): def test_napalm_getters_error(self, brigade): opt = {"path": THIS_DIR + "/test_napalm_getters_error"} + d = brigade.filter(name="dev3.group_2") + d.run(connections.napalm_connection, optional_args=opt) with pytest.raises(BrigadeExecutionError) as e: - brigade.filter(name="dev3.group_2").run(networking.napalm_get, - getters=["facts", - "interfaces"], - optional_args=opt) + d.run(networking.napalm_get, + getters=["facts", + "interfaces"]) assert len(e.value.failed_hosts) for exc in e.value.failed_hosts.values(): assert isinstance(exc, KeyError) From 78808a9bedac85cd2b610f94de47f0758b0f1356 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Sun, 10 Dec 2017 22:11:54 +0100 Subject: [PATCH 08/67] cleaning --- brigade/plugins/tasks/commands/remote_command.py | 3 --- brigade/plugins/tasks/files/sftp.py | 3 --- 2 files changed, 6 deletions(-) diff --git a/brigade/plugins/tasks/commands/remote_command.py b/brigade/plugins/tasks/commands/remote_command.py index 67620167..f7330ae6 100644 --- a/brigade/plugins/tasks/commands/remote_command.py +++ b/brigade/plugins/tasks/commands/remote_command.py @@ -11,9 +11,6 @@ def remote_command(task, command): """ Executes a command locally - Requires: - paramiko_connection - Arguments: command (``str``): command to execute diff --git a/brigade/plugins/tasks/files/sftp.py b/brigade/plugins/tasks/files/sftp.py index b286b61d..c010c5e1 100644 --- a/brigade/plugins/tasks/files/sftp.py +++ b/brigade/plugins/tasks/files/sftp.py @@ -107,9 +107,6 @@ def sftp(task, src, dst, action): """ Transfer files from/to the device using sftp protocol - Requires: - paramiko_connection - Example:: brigade.run(files.sftp, From 4d9d008052cf7a56e88d79ad3682d49e57980113 Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Mon, 11 Dec 2017 18:25:45 +0100 Subject: [PATCH 09/67] Add install section --- docs/tutorials/intro/install.rst | 59 ++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/docs/tutorials/intro/install.rst b/docs/tutorials/intro/install.rst index 0b6cbfe7..7d1b042d 100644 --- a/docs/tutorials/intro/install.rst +++ b/docs/tutorials/intro/install.rst @@ -1,2 +1,61 @@ Installing Brigade ================== + +Before you go ahead and install Brigade it's recommended to create your own Python virtualenv. That way you have complete control of your environment and you don't risk overwriting your systems Python environment. + +.. note:: + + This tutorial doesn't cover the creation of a Python virtual environment. The Python documentation offers a guide where you can learn more about `virtualenvs `_. We also won't cover the `installation of pip `_, but changes are that you already have pip on your system. + +Brigade is published to `PyPI `_ and can be installed like most other Python packages using the pip tool. You can verify that you have pip installed by typing: + +.. code-block:: bash + + pip --version + + pip 1.5.4 from /home/vagrant/brigade/local/lib/python2.7/site-packages (python 2.7) + +It could be that you need to use the pip3 binary instead of pip as pip3 is for Python 3 on some systems. + +So above we can see that we have pip installed. However the version 1.5.4 is pretty old. To save ourselves from trouble when installing packages we start by upgrading pip. + +.. code-block:: bash + + pip install "pip>=9.0.1" + + pip --version + + pip 9.0.1 from /home/vagrant/brigade/local/lib/python2.7/site-packages (python 2.7) + +That's more like it! The next step is to install Brigade. + +.. code-block:: bash + + pip install brigade + + Collecting brigade + [...] + Successfully installed MarkupSafe-1.0 + asn1crypto-0.23.0 bcrypt-3.1.4 brigade-0.0.5 + certifi-2017.11.5 cffi-1.11.2 chardet-3.0.4 + cryptography-2.1.4 enum34-1.1.6 future-0.16.0 + idna-2.6 ipaddress-1.0.18 jinja2-2.10 + jtextfsm-0.3.1 junos-eznc-2.1.7 lxml-4.1.1 + napalm-2.2.0 ncclient-0.5.3 netaddr-0.7.19 + netmiko-1.4.3 paramiko-2.4.0 pyIOSXR-0.52 + pyasn1-0.4.2 pycparser-2.18 pyeapi-0.8.1 + pynacl-1.2.1 pynxos-0.0.3 pyserial-3.4 + pyyaml-3.12 requests-2.18.4 scp-0.10.2 + six-1.11.0 urllib3-1.22 + +Please note that the above output has been abbreviated for readability. Your output will probably be longer. You should see that `brigade` is successfully installed. + +Now we can verify that Brigade is installed and that you are able to import the package from Python. + +.. code-block:: python + + python + >>>import brigade.core + >>> + +Great, now you're ready to create an inventory. From de4adde77fd212331b3384ee5ac8c21df482e2b0 Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Mon, 11 Dec 2017 19:14:51 +0100 Subject: [PATCH 10/67] Add tests for Sphinx --- tox.ini | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index f96e0ec3..9fd28d14 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27,py34,py35,py36 +envlist = py27,py34,py35,py36,docs [testenv] deps = @@ -9,3 +9,15 @@ deps = commands = py.test + +[testenv:docs] +basepython = python3 +deps= + sphinx + sphinx_rtd_theme +whitelist_externals = + cd + make +commands= + cd docs && make clean + sphinx-build -W -b html -d docs/_build/doctrees docs docs/_build/html From cc82feedb7261f586a6ec73be24aecb3a1116f12 Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Mon, 11 Dec 2017 20:28:24 +0100 Subject: [PATCH 11/67] Test Sphinx in all environments --- tox.ini | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/tox.ini b/tox.ini index 9fd28d14..c9151208 100644 --- a/tox.ini +++ b/tox.ini @@ -1,23 +1,13 @@ [tox] -envlist = py27,py34,py35,py36,docs +envlist = py27,py34,py35,py36 [testenv] deps = -rrequirements.txt -rrequirements-dev.txt git+https://github.com/ktbyers/netmiko.git@develop + -rdocs/requirements.txt commands = py.test - -[testenv:docs] -basepython = python3 -deps= - sphinx - sphinx_rtd_theme -whitelist_externals = - cd - make -commands= - cd docs && make clean sphinx-build -W -b html -d docs/_build/doctrees docs docs/_build/html From 4337a7bae8939138a352d8c26f3f92416242e2c4 Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Tue, 12 Dec 2017 17:59:26 +0100 Subject: [PATCH 12/67] Add __version__ --- brigade/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/brigade/__init__.py b/brigade/__init__.py index e69de29b..4e5de560 100644 --- a/brigade/__init__.py +++ b/brigade/__init__.py @@ -0,0 +1,6 @@ +import pkg_resources + +try: + __version__ = pkg_resources.get_distribution('brigade').version +except pkg_resources.DistributionNotFound: + __version__ = "Not installed" From c3955594c105dbab2dec5afd839c36c0e899bee1 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Wed, 13 Dec 2017 10:56:17 -0800 Subject: [PATCH 13/67] Netmiko connection --- .../tasks/connections/netmiko_connection.py | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 brigade/plugins/tasks/connections/netmiko_connection.py diff --git a/brigade/plugins/tasks/connections/netmiko_connection.py b/brigade/plugins/tasks/connections/netmiko_connection.py new file mode 100644 index 00000000..9364f22e --- /dev/null +++ b/brigade/plugins/tasks/connections/netmiko_connection.py @@ -0,0 +1,38 @@ +from netmiko import ConnectHandler + +napalm_to_netmiko_map = { + 'ios': 'cisco_ios', + 'nxos': 'cisco_nxos', + 'eos': 'arista_eos', + 'junos': 'juniper_junos', + 'iosxr': 'cisco_iosxr' +} + + +def netmiko_connection(task=None, host=None, **netmiko_args): + """Connect to the host using Netmiko and set the relevant connection in the connection map. + + Arguments: + **netmiko_args: All supported Netmiko ConnectHandler arguments + """ + if netmiko_args is None: + netmiko_args = {} + + if host is None: + host = task.host + + parameters = { + "host": host.host, + "username": host.username, + "password": host.password, + "port": host.ssh_port + } + if timeout is not None: + parameters['timeout'] = timeout + if host.nos is not None: + # Try to look it up in map, if it fails just return the host.nos + device_type = napalm_to_netmiko_map.get(host.nos, host.nos) + parameters['device_type'] = device_type + + parameters.update(**netmiko_args) + host.connections["netmiko"] = ConnectHandler(**parameters) From 0a40b349bbdb9943559ec048f2b2da0c9fb6bfdd Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Wed, 13 Dec 2017 11:32:18 -0800 Subject: [PATCH 14/67] Add Netmiko connection code --- brigade/core/inventory.py | 6 ++++-- brigade/plugins/tasks/connections/__init__.py | 2 ++ .../plugins/tasks/connections/netmiko_connection.py | 12 ++++++------ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/brigade/core/inventory.py b/brigade/core/inventory.py index c1210f9c..1dc81964 100644 --- a/brigade/core/inventory.py +++ b/brigade/core/inventory.py @@ -174,7 +174,7 @@ def nos(self): """Network OS the device is running. Defaults to ``brigade_nos``.""" return self.get("brigade_nos") - def get_connection(self, connection): + def get_connection(self, connection, **kwargs): """ This function will try to find an already established connection or call the task that establishes the connection if none is found. @@ -189,6 +189,7 @@ def get_connection(self, connection): Arguments: connection_name (str): Name of the connection, for instance, netmiko, paramiko, napalm... + **kwargs: Additional arguments that will be passed directly for connection creation. """ if connection not in self.connections: task_name = "{}_connection".format(connection) @@ -197,7 +198,8 @@ def get_connection(self, connection): except AttributeError: raise AttributeError("not sure how to establish a connection for {}".format( connection)) - task(host=self) + kwargs['host'] = self + task(**kwargs) return self.connections[connection] diff --git a/brigade/plugins/tasks/connections/__init__.py b/brigade/plugins/tasks/connections/__init__.py index 4a93caeb..6bca8d87 100644 --- a/brigade/plugins/tasks/connections/__init__.py +++ b/brigade/plugins/tasks/connections/__init__.py @@ -1,7 +1,9 @@ from .napalm_connection import napalm_connection +from .netmiko_connection import netmiko_connection from .paramiko_connection import paramiko_connection __all__ = ( "napalm_connection", + "netmiko_connection", "paramiko_connection", ) diff --git a/brigade/plugins/tasks/connections/netmiko_connection.py b/brigade/plugins/tasks/connections/netmiko_connection.py index 9364f22e..86f9f3b8 100644 --- a/brigade/plugins/tasks/connections/netmiko_connection.py +++ b/brigade/plugins/tasks/connections/netmiko_connection.py @@ -15,9 +15,6 @@ def netmiko_connection(task=None, host=None, **netmiko_args): Arguments: **netmiko_args: All supported Netmiko ConnectHandler arguments """ - if netmiko_args is None: - netmiko_args = {} - if host is None: host = task.host @@ -27,12 +24,15 @@ def netmiko_connection(task=None, host=None, **netmiko_args): "password": host.password, "port": host.ssh_port } - if timeout is not None: - parameters['timeout'] = timeout if host.nos is not None: - # Try to look it up in map, if it fails just return the host.nos + # Look device_type up in corresponding map, if no entry return the host.nos unmodified device_type = napalm_to_netmiko_map.get(host.nos, host.nos) parameters['device_type'] = device_type + # Both netmiko and brigade use host, allow passing of an alternately named host argument + if netmiko_args.get("netmiko_host"): + netmiko_host = netmiko_args.pop("netmiko_host") + netmiko_args['host'] = netmiko_host + parameters.update(**netmiko_args) host.connections["netmiko"] = ConnectHandler(**parameters) From 6d9b75a82077b1c06e28b2e988bf6c0ba4c6845b Mon Sep 17 00:00:00 2001 From: David Barroso Date: Wed, 13 Dec 2017 21:20:38 +0100 Subject: [PATCH 15/67] get_connection no longer establishes a connection automatically --- brigade/core/inventory.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/brigade/core/inventory.py b/brigade/core/inventory.py index c1210f9c..d80306bb 100644 --- a/brigade/core/inventory.py +++ b/brigade/core/inventory.py @@ -2,7 +2,6 @@ import logging from brigade.core import helpers -from brigade.plugins.tasks import connections logger = logging.getLogger("brigade") @@ -176,11 +175,7 @@ def nos(self): def get_connection(self, connection): """ - This function will try to find an already established connection - or call the task that establishes the connection if none is found. - In any case, it should always return an established connection or - an error if the connection is not already established and we don't - know of any task that could provide that type of connection. + This function will return an already established connection Raises: AttributeError: if it's unknown how to establish a connection for the given @@ -189,15 +184,16 @@ def get_connection(self, connection): Arguments: connection_name (str): Name of the connection, for instance, netmiko, paramiko, napalm... + + Returns: + An already established connection of type ``connection`` """ if connection not in self.connections: - task_name = "{}_connection".format(connection) - try: - task = getattr(connections, task_name) - except AttributeError: - raise AttributeError("not sure how to establish a connection for {}".format( - connection)) - task(host=self) + msg = ( + "Couldn't find an established connection for '{c}'. " + "Did you call '{c}_connection'?" + ).format(c=connection) + raise AttributeError(msg) return self.connections[connection] From 30d5359270ed562e34a1fb42149ef780b7524fad Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Wed, 13 Dec 2017 12:27:24 -0800 Subject: [PATCH 16/67] Make consistent change on get_connection --- brigade/core/inventory.py | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/brigade/core/inventory.py b/brigade/core/inventory.py index 1dc81964..46ab4d9c 100644 --- a/brigade/core/inventory.py +++ b/brigade/core/inventory.py @@ -174,32 +174,24 @@ def nos(self): """Network OS the device is running. Defaults to ``brigade_nos``.""" return self.get("brigade_nos") - def get_connection(self, connection, **kwargs): + def get_connection(self, connection): """ - This function will try to find an already established connection - or call the task that establishes the connection if none is found. - In any case, it should always return an established connection or - an error if the connection is not already established and we don't - know of any task that could provide that type of connection. - + This function will return an already established connection Raises: AttributeError: if it's unknown how to establish a connection for the given type - Arguments: connection_name (str): Name of the connection, for instance, netmiko, paramiko, napalm... - **kwargs: Additional arguments that will be passed directly for connection creation. + Returns: + An already established connection of type ``connection`` """ if connection not in self.connections: - task_name = "{}_connection".format(connection) - try: - task = getattr(connections, task_name) - except AttributeError: - raise AttributeError("not sure how to establish a connection for {}".format( - connection)) - kwargs['host'] = self - task(**kwargs) + msg = ( + "Couldn't find an established connection for '{c}'. " + "Did you call '{c}_connection'?" + ).format(c=connection) + raise AttributeError(msg) return self.connections[connection] From 0d53194decc9be793d140fc771904f3a6e77d836 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Wed, 13 Dec 2017 21:40:20 +0100 Subject: [PATCH 17/67] fix tests --- tests/conftest.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index abe65897..0b47b19a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,6 +4,7 @@ from brigade.core import Brigade from brigade.plugins.inventory.simple import SimpleInventory +from brigade.plugins.tasks.connections import paramiko_connection import pytest @@ -17,7 +18,7 @@ @pytest.fixture(scope="session", autouse=True) -def containers(request): +def a_containers(request): """Start/Stop containers needed for the tests.""" def fin(): logging.info("Stopping containers") @@ -44,4 +45,5 @@ def brigade(request): "{}/inventory_data/groups.yaml".format(dir_path)), dry_run=True, ) + brigade.run(paramiko_connection) return brigade From 883885ae70087650527086cfd23057574d66a8ac Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Wed, 13 Dec 2017 10:56:17 -0800 Subject: [PATCH 18/67] Netmiko connection --- .../tasks/connections/netmiko_connection.py | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 brigade/plugins/tasks/connections/netmiko_connection.py diff --git a/brigade/plugins/tasks/connections/netmiko_connection.py b/brigade/plugins/tasks/connections/netmiko_connection.py new file mode 100644 index 00000000..9364f22e --- /dev/null +++ b/brigade/plugins/tasks/connections/netmiko_connection.py @@ -0,0 +1,38 @@ +from netmiko import ConnectHandler + +napalm_to_netmiko_map = { + 'ios': 'cisco_ios', + 'nxos': 'cisco_nxos', + 'eos': 'arista_eos', + 'junos': 'juniper_junos', + 'iosxr': 'cisco_iosxr' +} + + +def netmiko_connection(task=None, host=None, **netmiko_args): + """Connect to the host using Netmiko and set the relevant connection in the connection map. + + Arguments: + **netmiko_args: All supported Netmiko ConnectHandler arguments + """ + if netmiko_args is None: + netmiko_args = {} + + if host is None: + host = task.host + + parameters = { + "host": host.host, + "username": host.username, + "password": host.password, + "port": host.ssh_port + } + if timeout is not None: + parameters['timeout'] = timeout + if host.nos is not None: + # Try to look it up in map, if it fails just return the host.nos + device_type = napalm_to_netmiko_map.get(host.nos, host.nos) + parameters['device_type'] = device_type + + parameters.update(**netmiko_args) + host.connections["netmiko"] = ConnectHandler(**parameters) From 72d4658e5721c64b4f8fb0ce4a317f7f931c3be1 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Wed, 13 Dec 2017 11:32:18 -0800 Subject: [PATCH 19/67] Add Netmiko connection code --- brigade/core/inventory.py | 3 --- brigade/plugins/tasks/connections/__init__.py | 2 ++ .../plugins/tasks/connections/netmiko_connection.py | 12 ++++++------ 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/brigade/core/inventory.py b/brigade/core/inventory.py index d80306bb..15de8a68 100644 --- a/brigade/core/inventory.py +++ b/brigade/core/inventory.py @@ -176,15 +176,12 @@ def nos(self): def get_connection(self, connection): """ This function will return an already established connection - Raises: AttributeError: if it's unknown how to establish a connection for the given type - Arguments: connection_name (str): Name of the connection, for instance, netmiko, paramiko, napalm... - Returns: An already established connection of type ``connection`` """ diff --git a/brigade/plugins/tasks/connections/__init__.py b/brigade/plugins/tasks/connections/__init__.py index 4a93caeb..6bca8d87 100644 --- a/brigade/plugins/tasks/connections/__init__.py +++ b/brigade/plugins/tasks/connections/__init__.py @@ -1,7 +1,9 @@ from .napalm_connection import napalm_connection +from .netmiko_connection import netmiko_connection from .paramiko_connection import paramiko_connection __all__ = ( "napalm_connection", + "netmiko_connection", "paramiko_connection", ) diff --git a/brigade/plugins/tasks/connections/netmiko_connection.py b/brigade/plugins/tasks/connections/netmiko_connection.py index 9364f22e..86f9f3b8 100644 --- a/brigade/plugins/tasks/connections/netmiko_connection.py +++ b/brigade/plugins/tasks/connections/netmiko_connection.py @@ -15,9 +15,6 @@ def netmiko_connection(task=None, host=None, **netmiko_args): Arguments: **netmiko_args: All supported Netmiko ConnectHandler arguments """ - if netmiko_args is None: - netmiko_args = {} - if host is None: host = task.host @@ -27,12 +24,15 @@ def netmiko_connection(task=None, host=None, **netmiko_args): "password": host.password, "port": host.ssh_port } - if timeout is not None: - parameters['timeout'] = timeout if host.nos is not None: - # Try to look it up in map, if it fails just return the host.nos + # Look device_type up in corresponding map, if no entry return the host.nos unmodified device_type = napalm_to_netmiko_map.get(host.nos, host.nos) parameters['device_type'] = device_type + # Both netmiko and brigade use host, allow passing of an alternately named host argument + if netmiko_args.get("netmiko_host"): + netmiko_host = netmiko_args.pop("netmiko_host") + netmiko_args['host'] = netmiko_host + parameters.update(**netmiko_args) host.connections["netmiko"] = ConnectHandler(**parameters) From 2768c6e537beafa1aabffd3e2a63d7ed8f608aec Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Wed, 13 Dec 2017 13:11:40 -0800 Subject: [PATCH 20/67] Fixing docstring --- brigade/core/inventory.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/brigade/core/inventory.py b/brigade/core/inventory.py index 15de8a68..d80306bb 100644 --- a/brigade/core/inventory.py +++ b/brigade/core/inventory.py @@ -176,12 +176,15 @@ def nos(self): def get_connection(self, connection): """ This function will return an already established connection + Raises: AttributeError: if it's unknown how to establish a connection for the given type + Arguments: connection_name (str): Name of the connection, for instance, netmiko, paramiko, napalm... + Returns: An already established connection of type ``connection`` """ From 97a7e34982022b0e13f50bb95ba149de313e7c66 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Wed, 13 Dec 2017 13:38:44 -0800 Subject: [PATCH 21/67] Adding message if someone passes in a netmiko 'host' --- brigade/plugins/tasks/connections/netmiko_connection.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/brigade/plugins/tasks/connections/netmiko_connection.py b/brigade/plugins/tasks/connections/netmiko_connection.py index 86f9f3b8..737caf07 100644 --- a/brigade/plugins/tasks/connections/netmiko_connection.py +++ b/brigade/plugins/tasks/connections/netmiko_connection.py @@ -18,6 +18,14 @@ def netmiko_connection(task=None, host=None, **netmiko_args): if host is None: host = task.host + try: + host.host + except AttributeError: + msg = "Both Netmiko and Brigade have a host argument. Use the 'netmiko_host' argument " \ + "or the 'ip' argument to specify a device to connect to (if not specifying in " \ + "Brigade's inventory)." + raise AttributeError(msg) + parameters = { "host": host.host, "username": host.username, From 73b8ebe7ee1c9ef57f4f7e7433447901461ca670 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Wed, 13 Dec 2017 18:26:17 -0800 Subject: [PATCH 22/67] Adding docstring fixes --- brigade/plugins/tasks/connections/napalm_connection.py | 2 +- brigade/plugins/tasks/connections/netmiko_connection.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/brigade/plugins/tasks/connections/napalm_connection.py b/brigade/plugins/tasks/connections/napalm_connection.py index 7cd17f79..e5764df7 100644 --- a/brigade/plugins/tasks/connections/napalm_connection.py +++ b/brigade/plugins/tasks/connections/napalm_connection.py @@ -3,7 +3,7 @@ def napalm_connection(task=None, host=None, timeout=60, optional_args=None): """ - This tasks connects to the device with paramiko to the device and sets the + This tasks connects to the device using the NAPALM driver and sets the relevant connection. Arguments: diff --git a/brigade/plugins/tasks/connections/netmiko_connection.py b/brigade/plugins/tasks/connections/netmiko_connection.py index 737caf07..583fb02a 100644 --- a/brigade/plugins/tasks/connections/netmiko_connection.py +++ b/brigade/plugins/tasks/connections/netmiko_connection.py @@ -14,6 +14,9 @@ def netmiko_connection(task=None, host=None, **netmiko_args): Arguments: **netmiko_args: All supported Netmiko ConnectHandler arguments + + Note, both Netmiko and Brigade have a host argument. Use the ``netmiko_host`` argument to + pass in a host (if bypassing Brigade inventory). """ if host is None: host = task.host From 96f3120f9701b8a61677d5756194ee48b019858f Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Thu, 14 Dec 2017 18:44:18 +0100 Subject: [PATCH 23/67] Add config class to tweak common parameters --- .gitignore | 1 + brigade/core/__init__.py | 25 ++++---- brigade/core/configuration.py | 64 +++++++++++++++++++ .../tasks/connections/paramiko_connection.py | 4 +- .../configuration-parameters.j2 | 25 ++++++++ docs/conf.py | 25 ++++++++ docs/ref/api/configuration.rst | 9 +++ docs/ref/api/index.rst | 1 + docs/ref/configuration/generated/.placeholder | 0 docs/ref/configuration/index.rst | 10 +++ docs/ref/index.rst | 6 ++ 11 files changed, 155 insertions(+), 15 deletions(-) create mode 100644 brigade/core/configuration.py create mode 100644 docs/_data_templates/configuration-parameters.j2 create mode 100644 docs/ref/api/configuration.rst create mode 100644 docs/ref/configuration/generated/.placeholder create mode 100644 docs/ref/configuration/index.rst diff --git a/.gitignore b/.gitignore index 73d0e20b..1bde5d19 100644 --- a/.gitignore +++ b/.gitignore @@ -62,6 +62,7 @@ instance/ # Sphinx documentation docs/_build/ +docs/ref/configuration/generated/*.rst # PyBuilder target/ diff --git a/brigade/core/__init__.py b/brigade/core/__init__.py index 705c1981..8962ae3c 100644 --- a/brigade/core/__init__.py +++ b/brigade/core/__init__.py @@ -3,6 +3,7 @@ import traceback from multiprocessing.dummy import Pool +from brigade.core.configuration import Config from brigade.core.task import AggregatedResult, Task @@ -42,24 +43,24 @@ class Brigade(object): Arguments: inventory (:obj:`brigade.core.inventory.Inventory`): Inventory to work with dry_run(``bool``): Whether if we are testing the changes or not - num_workers(``int``): How many hosts run in parallel - raise_on_error (``bool``): If set to ``True``, :meth:`run` method of will - raise an exception if at least a host failed. + config (:obj:`brigade.core.configuration.Config`): Configuration object + config_file (``str``): Path to Yaml configuration file Attributes: inventory (:obj:`brigade.core.inventory.Inventory`): Inventory to work with dry_run(``bool``): Whether if we are testing the changes or not - num_workers(``int``): How many hosts run in parallel - raise_on_error (``bool``): If set to ``True``, :meth:`run` method of will - raise an exception if at least a host failed. + config (:obj:`brigade.core.configuration.Config`): Configuration parameters """ - def __init__(self, inventory, dry_run, num_workers=20, raise_on_error=True): + def __init__(self, inventory, dry_run, + config=None, config_file=None): self.inventory = inventory self.dry_run = dry_run - self.num_workers = num_workers - self.raise_on_error = raise_on_error + if config_file: + self.config = Config(config_file=config_file) + else: + self.config = config or Config() format = "\033[31m%(asctime)s - %(name)s - %(levelname)s" format += " - %(funcName)20s() - %(message)s\033[0m" @@ -123,19 +124,19 @@ def run(self, task, num_workers=None, **kwargs): Raises: :obj:`brigade.core.exceptions.BrigadeExceptionError`: if at least a task fails - and self.raise_on_error is set to ``True`` + and self.config.raise_on_error is set to ``True`` Returns: :obj:`brigade.core.task.AggregatedResult`: results of each execution """ - num_workers = num_workers or self.num_workers + num_workers = num_workers or self.config.num_workers if num_workers == 1: result = self._run_serial(task, **kwargs) else: result = self._run_parallel(task, num_workers, **kwargs) - if self.raise_on_error: + if self.config.raise_on_error: result.raise_on_error() return result diff --git a/brigade/core/configuration.py b/brigade/core/configuration.py new file mode 100644 index 00000000..c0b38c04 --- /dev/null +++ b/brigade/core/configuration.py @@ -0,0 +1,64 @@ +import ast +import os + + +import yaml + + +CONF = { + 'num_workers': { + 'description': 'Number of Brigade worker processes that are run at the same time, ' + 'configuration can be overridden on individual tasks by using the ' + '`num_workers` argument to (:obj:`brigade.core.Brigade.run`)', + 'type': 'int', + 'default': 20, + }, + 'raise_on_error': { + 'description': "If set to ``True``, (:obj:`brigade.core.Brigade.run`) method of will raise " + "an exception if at least a host failed.", + 'type': 'bool', + 'default': True, + }, + 'ssh_config_file': { + 'description': 'User ssh_config_file', + 'type': 'str', + 'default': os.path.join(os.path.expanduser("~"), ".ssh", "config"), + 'default_doc': '~/.ssh/config' + }, +} + +types = { + 'int': int, + 'str': str, +} + + +class Config: + """ + This object handles the configuration of Brigade. + + Arguments: + config_file(``str``): Yaml configuration file. + """ + + def __init__(self, config_file=None): + + if config_file: + with open(config_file, 'r') as f: + c = yaml.load(f.read()) + else: + c = {} + + self._assign_properties(c) + + def _assign_properties(self, c): + + for p in CONF: + env = CONF[p].get('env') or 'BRIGADE_' + p.upper() + v = os.environ.get(env) or c.get(p) + v = v if v is not None else CONF[p]['default'] + if CONF[p]['type'] == 'bool': + v = ast.literal_eval(str(v).title()) + else: + v = types[CONF[p]['type']](v) + setattr(self, p, v) diff --git a/brigade/plugins/tasks/connections/paramiko_connection.py b/brigade/plugins/tasks/connections/paramiko_connection.py index 81836997..73e1111f 100644 --- a/brigade/plugins/tasks/connections/paramiko_connection.py +++ b/brigade/plugins/tasks/connections/paramiko_connection.py @@ -11,14 +11,12 @@ def paramiko_connection(task=None, host=None): if host is None: host = task.host - # TODO configurable - ssh_config_file = os.path.join(os.path.expanduser("~"), ".ssh", "config") - client = paramiko.SSHClient() client._policy = paramiko.WarningPolicy() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh_config = paramiko.SSHConfig() + ssh_config_file = task.brigade.config.ssh_config_file if os.path.exists(ssh_config_file): with open(ssh_config_file) as f: ssh_config.parse(f) diff --git a/docs/_data_templates/configuration-parameters.j2 b/docs/_data_templates/configuration-parameters.j2 new file mode 100644 index 00000000..0f06931a --- /dev/null +++ b/docs/_data_templates/configuration-parameters.j2 @@ -0,0 +1,25 @@ +The configuration parameters will be set by the :doc:`Brigade.core.configuration.Config ` class. + +{% for k, v in params|dictsort %} +---------- + +{{ k }} +---------------------------------- + + +.. raw:: html + + + + + + + + + + +
Environment variableTypeDefault
{{ v['env'] or 'BRIGADE_' + k|upper }}{{ v['type'] }}{{ v['default_doc'] or v['default'] }}
+ +{{ v['description'] }} + +{% endfor %} \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index 11fc5d3a..e7b67dbb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -19,11 +19,15 @@ # import os import sys +from jinja2 import Environment, FileSystemLoader sys.path.insert(0, os.path.abspath('../')) +from brigade.core.configuration import CONF # noqa + # -- General configuration ------------------------------------------------ +BASEPATH = os.path.dirname(__file__) # If your documentation needs a minimal Sphinx version, state it here. # @@ -166,3 +170,24 @@ author, 'brigade', 'One line description of project.', 'Miscellaneous'), ] + + +def build_configuration_parameters(app): + """Create documentation for configuration parameters.""" + + env = Environment(loader=FileSystemLoader("{0}/_data_templates".format(BASEPATH))) + template_file = env.get_template("configuration-parameters.j2") + data = {} + data['params'] = CONF + rendered_template = template_file.render(**data) + output_dir = '{0}/ref/configuration/generated'.format(BASEPATH) + with open('{}/parameters.rst'.format(output_dir), 'w') as f: + f.write(rendered_template) + + +def setup(app): + """Map methods to states of the documentation build.""" + app.connect('builder-inited', build_configuration_parameters) + + +build_configuration_parameters(None) diff --git a/docs/ref/api/configuration.rst b/docs/ref/api/configuration.rst new file mode 100644 index 00000000..6bb14964 --- /dev/null +++ b/docs/ref/api/configuration.rst @@ -0,0 +1,9 @@ +Configuration +############# + + +.. autoclass:: brigade.core.configuration.Config + :members: + :undoc-members: + +The attributes for the Config object will be the Brigade configuration parameters. For a list of available parameters see :doc:`Brigade configuration parameters ` \ No newline at end of file diff --git a/docs/ref/api/index.rst b/docs/ref/api/index.rst index 26d544bb..a53ebe21 100644 --- a/docs/ref/api/index.rst +++ b/docs/ref/api/index.rst @@ -6,6 +6,7 @@ Brigade API Reference :caption: Brigade API brigade + configuration inventory task exceptions \ No newline at end of file diff --git a/docs/ref/configuration/generated/.placeholder b/docs/ref/configuration/generated/.placeholder new file mode 100644 index 00000000..e69de29b diff --git a/docs/ref/configuration/index.rst b/docs/ref/configuration/index.rst new file mode 100644 index 00000000..4b724985 --- /dev/null +++ b/docs/ref/configuration/index.rst @@ -0,0 +1,10 @@ +Brigade Configuration +===================== + +Each configuration parameter are applied in the following order: + +1. Environment variable +2. Parameter in configuration file / object +3. Default value + +.. include:: generated/parameters.rst \ No newline at end of file diff --git a/docs/ref/index.rst b/docs/ref/index.rst index c4d1551c..e22b5322 100644 --- a/docs/ref/index.rst +++ b/docs/ref/index.rst @@ -7,6 +7,12 @@ Reference Guides API +.. toctree:: + :maxdepth: 2 + :caption: Configuration + + configuration/index + .. toctree:: :maxdepth: 2 :caption: Plugins From 8c30630645894605ee919486ff9a1af007bd94c6 Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Fri, 15 Dec 2017 09:47:18 +0100 Subject: [PATCH 24/67] Remove develop branch of netmiko (#49) --- requirements.txt | 1 + tox.ini | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8e74d76b..13de20d1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ pyyaml jinja2 napalm +netmiko paramiko future diff --git a/tox.ini b/tox.ini index c9151208..25138a36 100644 --- a/tox.ini +++ b/tox.ini @@ -5,7 +5,6 @@ envlist = py27,py34,py35,py36 deps = -rrequirements.txt -rrequirements-dev.txt - git+https://github.com/ktbyers/netmiko.git@develop -rdocs/requirements.txt commands = From 823d4107999e486f9c480237af0320a5760a7438 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Fri, 15 Dec 2017 09:47:42 +0100 Subject: [PATCH 25/67] removing host from connection tasks as it's no longer needed (#48) --- .../tasks/connections/napalm_connection.py | 19 ++++++++-------- .../tasks/connections/netmiko_connection.py | 22 ++----------------- .../tasks/connections/paramiko_connection.py | 5 ++--- 3 files changed, 13 insertions(+), 33 deletions(-) diff --git a/brigade/plugins/tasks/connections/napalm_connection.py b/brigade/plugins/tasks/connections/napalm_connection.py index e5764df7..22ee8821 100644 --- a/brigade/plugins/tasks/connections/napalm_connection.py +++ b/brigade/plugins/tasks/connections/napalm_connection.py @@ -1,28 +1,27 @@ from napalm import get_network_driver -def napalm_connection(task=None, host=None, timeout=60, optional_args=None): +def napalm_connection(task=None, timeout=60, optional_args=None): """ This tasks connects to the device using the NAPALM driver and sets the relevant connection. Arguments: timeout (int, optional): defaults to 60 - optional_args (dict, optional): defaults to ``{"port": task.host["napalm_port"]}`` + optional_args (dict, optional): defaults to ``{"port": task.host["network_api_port"]}`` """ - if host is None: - host = task.host + host = task.host parameters = { - "hostname": task.host.host, - "username": task.host.username, - "password": task.host.password, + "hostname": host.host, + "username": host.username, + "password": host.password, "timeout": timeout, "optional_args": optional_args or {}, } - if "port" not in parameters["optional_args"] and task.host.network_api_port: - parameters["optional_args"]["port"] = task.host.network_api_port - network_driver = get_network_driver(task.host.nos) + if "port" not in parameters["optional_args"] and host.network_api_port: + parameters["optional_args"]["port"] = host.network_api_port + network_driver = get_network_driver(host.nos) host.connections["napalm"] = network_driver(**parameters) host.connections["napalm"].open() diff --git a/brigade/plugins/tasks/connections/netmiko_connection.py b/brigade/plugins/tasks/connections/netmiko_connection.py index 583fb02a..e4ba0b46 100644 --- a/brigade/plugins/tasks/connections/netmiko_connection.py +++ b/brigade/plugins/tasks/connections/netmiko_connection.py @@ -9,26 +9,13 @@ } -def netmiko_connection(task=None, host=None, **netmiko_args): +def netmiko_connection(task=None, **netmiko_args): """Connect to the host using Netmiko and set the relevant connection in the connection map. Arguments: **netmiko_args: All supported Netmiko ConnectHandler arguments - - Note, both Netmiko and Brigade have a host argument. Use the ``netmiko_host`` argument to - pass in a host (if bypassing Brigade inventory). """ - if host is None: - host = task.host - - try: - host.host - except AttributeError: - msg = "Both Netmiko and Brigade have a host argument. Use the 'netmiko_host' argument " \ - "or the 'ip' argument to specify a device to connect to (if not specifying in " \ - "Brigade's inventory)." - raise AttributeError(msg) - + host = task.host parameters = { "host": host.host, "username": host.username, @@ -40,10 +27,5 @@ def netmiko_connection(task=None, host=None, **netmiko_args): device_type = napalm_to_netmiko_map.get(host.nos, host.nos) parameters['device_type'] = device_type - # Both netmiko and brigade use host, allow passing of an alternately named host argument - if netmiko_args.get("netmiko_host"): - netmiko_host = netmiko_args.pop("netmiko_host") - netmiko_args['host'] = netmiko_host - parameters.update(**netmiko_args) host.connections["netmiko"] = ConnectHandler(**parameters) diff --git a/brigade/plugins/tasks/connections/paramiko_connection.py b/brigade/plugins/tasks/connections/paramiko_connection.py index 73e1111f..6486a1ca 100644 --- a/brigade/plugins/tasks/connections/paramiko_connection.py +++ b/brigade/plugins/tasks/connections/paramiko_connection.py @@ -3,13 +3,12 @@ import paramiko -def paramiko_connection(task=None, host=None): +def paramiko_connection(task=None): """ This tasks connects to the device with paramiko to the device and sets the relevant connection. """ - if host is None: - host = task.host + host = task.host client = paramiko.SSHClient() client._policy = paramiko.WarningPolicy() From 56447da54bafdf33e3d4e7bdb20552392b03e377 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Fri, 15 Dec 2017 14:51:24 +0100 Subject: [PATCH 26/67] added nsot's inventory plugin and transform_function to inventory (#44) --- Makefile | 7 ++ brigade/core/__init__.py | 2 +- brigade/core/inventory.py | 9 +- brigade/plugins/inventory/nsot.py | 52 ++++++++++++ brigade/plugins/inventory/simple.py | 4 +- docs/howto/transforming_inventory_data.rst | 25 ++++++ docs/howto/writing_a_custom_inventory.rst | 48 +++++++++++ docs/ref/inventory/index.rst | 9 +- docs/ref/inventory/nsot.rst | 6 ++ docs/ref/inventory/simple.rst | 6 ++ docs/ref/tasks/connections.rst | 6 ++ docs/ref/tasks/index.rst | 1 + requirements.txt | 1 + tests/inventory_data/nsot/nsot.sh | 79 ++++++++++++++++++ tests/inventory_data/nsot/nsot.sqlite3 | Bin 0 -> 220160 bytes tests/{tasks => plugins}/__init__.py | 0 .../inventory}/__init__.py | 0 tests/plugins/inventory/test_nsot.py | 49 +++++++++++ .../{tasks/data => plugins/tasks}/__init__.py | 0 .../tasks/commands}/__init__.py | 0 .../tasks/commands/test_command.py | 0 .../tasks/commands/test_remote_command.py | 0 .../tasks/data}/__init__.py | 0 .../tasks/data/test_data/broken.json | 0 .../tasks/data/test_data/broken.yaml | 0 .../tasks/data/test_data/simple.json | 0 .../tasks/data/test_data/simple.yaml | 0 .../tasks/data/test_load_json.py | 0 .../tasks/data/test_load_yaml.py | 0 .../text => plugins/tasks/files}/__init__.py | 0 tests/{ => plugins}/tasks/files/test_sftp.py | 0 tests/plugins/tasks/networking/__init__.py | 0 .../test_napalm_cli/cli.1.show_interfaces.1 | 0 .../test_napalm_cli/cli.1.show_version.0 | 0 .../step1/commit_config.1 | 0 .../step1/compare_config.1 | 0 .../step1/load_merge_candidate.1 | 0 .../step2/compare_config.1 | 0 .../step2/discard_config.1 | 0 .../step2/load_merge_candidate.1 | 0 .../compare_config.1 | 0 .../discard_config.1 | 0 .../load_merge_candidate.1 | 0 .../load_merge_candidate.1 | 0 .../test_napalm_getters/get_facts.1 | 0 .../test_napalm_getters/get_interfaces.1 | 0 .../test_napalm_getters_error/get_facts.1 | 0 .../get_interfaces.1 | 0 .../tasks/networking/test_napalm_cli.py | 0 .../tasks/networking/test_napalm_configure.py | 0 .../tasks/networking/test_napalm_get.py | 0 .../tasks/networking/test_netmiko_run.py | 0 .../tasks/networking/test_tcp_ping.py | 2 +- tests/plugins/tasks/text/__init__.py | 0 .../tasks/text/test_data/broken.j2 | 0 .../tasks/text/test_data/simple.j2 | 0 .../tasks/text/test_template_file.py | 0 .../tasks/text/test_template_string.py | 0 tox.ini | 1 + 59 files changed, 299 insertions(+), 8 deletions(-) create mode 100644 Makefile create mode 100644 brigade/plugins/inventory/nsot.py create mode 100644 docs/howto/transforming_inventory_data.rst create mode 100644 docs/howto/writing_a_custom_inventory.rst create mode 100644 docs/ref/inventory/nsot.rst create mode 100644 docs/ref/inventory/simple.rst create mode 100644 docs/ref/tasks/connections.rst create mode 100644 tests/inventory_data/nsot/nsot.sh create mode 100644 tests/inventory_data/nsot/nsot.sqlite3 rename tests/{tasks => plugins}/__init__.py (100%) rename tests/{tasks/commands => plugins/inventory}/__init__.py (100%) create mode 100644 tests/plugins/inventory/test_nsot.py rename tests/{tasks/data => plugins/tasks}/__init__.py (100%) rename tests/{tasks/files => plugins/tasks/commands}/__init__.py (100%) rename tests/{ => plugins}/tasks/commands/test_command.py (100%) rename tests/{ => plugins}/tasks/commands/test_remote_command.py (100%) rename tests/{tasks/networking => plugins/tasks/data}/__init__.py (100%) rename tests/{ => plugins}/tasks/data/test_data/broken.json (100%) rename tests/{ => plugins}/tasks/data/test_data/broken.yaml (100%) rename tests/{ => plugins}/tasks/data/test_data/simple.json (100%) rename tests/{ => plugins}/tasks/data/test_data/simple.yaml (100%) rename tests/{ => plugins}/tasks/data/test_load_json.py (100%) rename tests/{ => plugins}/tasks/data/test_load_yaml.py (100%) rename tests/{tasks/text => plugins/tasks/files}/__init__.py (100%) rename tests/{ => plugins}/tasks/files/test_sftp.py (100%) create mode 100644 tests/plugins/tasks/networking/__init__.py rename tests/{ => plugins}/tasks/networking/mocked/napalm_cli/test_napalm_cli/cli.1.show_interfaces.1 (100%) rename tests/{ => plugins}/tasks/networking/mocked/napalm_cli/test_napalm_cli/cli.1.show_version.0 (100%) rename tests/{ => plugins}/tasks/networking/mocked/napalm_configure/test_napalm_configure_change_commit/step1/commit_config.1 (100%) rename tests/{ => plugins}/tasks/networking/mocked/napalm_configure/test_napalm_configure_change_commit/step1/compare_config.1 (100%) rename tests/{ => plugins}/tasks/networking/mocked/napalm_configure/test_napalm_configure_change_commit/step1/load_merge_candidate.1 (100%) rename tests/{ => plugins}/tasks/networking/mocked/napalm_configure/test_napalm_configure_change_commit/step2/compare_config.1 (100%) rename tests/{ => plugins}/tasks/networking/mocked/napalm_configure/test_napalm_configure_change_commit/step2/discard_config.1 (100%) rename tests/{ => plugins}/tasks/networking/mocked/napalm_configure/test_napalm_configure_change_commit/step2/load_merge_candidate.1 (100%) rename tests/{ => plugins}/tasks/networking/mocked/napalm_configure/test_napalm_configure_change_dry_run/compare_config.1 (100%) rename tests/{ => plugins}/tasks/networking/mocked/napalm_configure/test_napalm_configure_change_dry_run/discard_config.1 (100%) rename tests/{ => plugins}/tasks/networking/mocked/napalm_configure/test_napalm_configure_change_dry_run/load_merge_candidate.1 (100%) rename tests/{ => plugins}/tasks/networking/mocked/napalm_configure/test_napalm_configure_change_error/load_merge_candidate.1 (100%) rename tests/{ => plugins}/tasks/networking/mocked/napalm_get/test_napalm_getters/get_facts.1 (100%) rename tests/{ => plugins}/tasks/networking/mocked/napalm_get/test_napalm_getters/get_interfaces.1 (100%) rename tests/{ => plugins}/tasks/networking/mocked/napalm_get/test_napalm_getters_error/get_facts.1 (100%) rename tests/{ => plugins}/tasks/networking/mocked/napalm_get/test_napalm_getters_error/get_interfaces.1 (100%) rename tests/{ => plugins}/tasks/networking/test_napalm_cli.py (100%) rename tests/{ => plugins}/tasks/networking/test_napalm_configure.py (100%) rename tests/{ => plugins}/tasks/networking/test_napalm_get.py (100%) rename tests/{ => plugins}/tasks/networking/test_netmiko_run.py (100%) rename tests/{ => plugins}/tasks/networking/test_tcp_ping.py (96%) create mode 100644 tests/plugins/tasks/text/__init__.py rename tests/{ => plugins}/tasks/text/test_data/broken.j2 (100%) rename tests/{ => plugins}/tasks/text/test_data/simple.j2 (100%) rename tests/{ => plugins}/tasks/text/test_template_file.py (100%) rename tests/{ => plugins}/tasks/text/test_template_string.py (100%) diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..53536625 --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +.PHONY: start_nsot +start_nsot: + docker run -v $(PWD)/tests/inventory_data/nsot/nsot.sqlite3:/nsot.sqlite3 -p 8990:8990 -d --name=nsot nsot/nsot start --noinput + +.PHONY: stop_nsot +stop_nsot: + docker rm -f nsot diff --git a/brigade/core/__init__.py b/brigade/core/__init__.py index 8962ae3c..7e724a14 100644 --- a/brigade/core/__init__.py +++ b/brigade/core/__init__.py @@ -123,7 +123,7 @@ def run(self, task, num_workers=None, **kwargs): **kwargs: additional argument to pass to ``task`` when calling it Raises: - :obj:`brigade.core.exceptions.BrigadeExceptionError`: if at least a task fails + :obj:`brigade.core.exceptions.BrigadeExecutionError`: if at least a task fails and self.config.raise_on_error is set to ``True`` Returns: diff --git a/brigade/core/inventory.py b/brigade/core/inventory.py index d80306bb..a32c4171 100644 --- a/brigade/core/inventory.py +++ b/brigade/core/inventory.py @@ -211,13 +211,16 @@ class Inventory(object): representing the host data. groups (dict): keys are group names and values are either :obj:`Group` or a dict representing the group data. + transform_function (callable): we will call this function for each host. This is useful + to manipulate host data and make it more consumable. For instance, if your inventory + has a "user" attribute you could use this function to map it to "brigade_user" Attributes: hosts (dict): keys are hostnames and values are :obj:`Host`. groups (dict): keys are group names and the values are :obj:`Group`. """ - def __init__(self, hosts, groups=None, data=None, host_data=None): + def __init__(self, hosts, groups=None, data=None, host_data=None, transform_function=None): self.data = data or {} groups = groups or {} @@ -235,6 +238,10 @@ def __init__(self, hosts, groups=None, data=None, host_data=None): for n, h in hosts.items(): if isinstance(h, dict): h = Host(name=n, **h) + + if transform_function: + transform_function(h) + if h.group is not None and not isinstance(h.group, Group): h.group = self.groups[h.group] self.hosts[n] = h diff --git a/brigade/plugins/inventory/nsot.py b/brigade/plugins/inventory/nsot.py new file mode 100644 index 00000000..b0d2ce3c --- /dev/null +++ b/brigade/plugins/inventory/nsot.py @@ -0,0 +1,52 @@ +import os +from builtins import super + +from brigade.core.inventory import Inventory + +import requests + + +class NSOTInventory(Inventory): + """ + Inventory plugin that uses `nsot `_ as backend. + + Note: + An extra attribute ``site`` will be assigned to the host. The value will be + the name of the site the host belongs to. + + Environment Variables: + * ``NSOT_URL``: URL to nsot's API (defaults to ``http://localhost:8990/api``) + * ``NSOT_EMAIL``: email for authentication (defaults to admin@acme.com) + + Arguments: + flatten_attributes (bool): Assign host attributes to the root object. Useful + for filtering hosts. + """ + + def __init__(self, flatten_attributes=True, **kwargs): + NSOT_URL = os.environ.get('NSOT_URL', 'http://localhost:8990/api') + NSOT_EMAIL = os.environ.get('NSOT_EMAIL', 'admin@acme.com') + + headers = {'X-NSoT-Email': NSOT_EMAIL} + devices = requests.get('{}/devices'.format(NSOT_URL), headers=headers).json() + sites = requests.get('{}/sites'.format(NSOT_URL), headers=headers).json() + interfaces = requests.get('{}/interfaces'.format(NSOT_URL), headers=headers).json() + + # We resolve site_id and assign "site" variable with the name of the site + for d in devices: + d['site'] = sites[d['site_id'] - 1]['name'] + d['interfaces'] = {} + + if flatten_attributes: + # We assign attributes to the root + for k, v in d.pop('attributes').items(): + d[k] = v + + # We assign the interfaces to the hosts + for i in interfaces: + devices[i['device'] - 1]['interfaces'][i['name']] = i + + # Finally the inventory expects a dict of hosts where the key is the hostname + devices = {d['hostname']: d for d in devices} + + super().__init__(devices, None, **kwargs) diff --git a/brigade/plugins/inventory/simple.py b/brigade/plugins/inventory/simple.py index a6ab05cb..e04e733e 100644 --- a/brigade/plugins/inventory/simple.py +++ b/brigade/plugins/inventory/simple.py @@ -103,7 +103,7 @@ class SimpleInventory(Inventory): group: all """ - def __init__(self, host_file, group_file=None): + def __init__(self, host_file, group_file=None, **kwargs): with open(host_file, "r") as f: hosts = yaml.load(f.read()) @@ -113,4 +113,4 @@ def __init__(self, host_file, group_file=None): else: groups = {} - super().__init__(hosts, groups) + super().__init__(hosts, groups, **kwargs) diff --git a/docs/howto/transforming_inventory_data.rst b/docs/howto/transforming_inventory_data.rst new file mode 100644 index 00000000..b6195ffd --- /dev/null +++ b/docs/howto/transforming_inventory_data.rst @@ -0,0 +1,25 @@ +Transforming Inventory Data +=========================== + +Imagine your data looks like:: + + host1: + username: my_user + password: my_password + host2: + username: my_user + password: my_password + +It turns out brigade is going to look for ``brigade_username`` and ``brigade_password`` to use as credentials. You may not want to change the data in your backend and you may not want to write a custom inventory plugin just to accommodate this difference. Fortunately, ``brigade`` has you covered. You can write a function to do all the data manipulations you want and pass it to any inventory plugin. For instance:: + + def adapt_host_data(host): + host.data["brigade_username"] = host.data["username"] + host.data["brigade_password"] = host.data["password"] + + + inv = NSOTInventory(transform_function=adapt_host_data) + brigade = Brigade(inventory=inv) + +What's going to happen is that the inventory is going to create the :obj:`brigade.core.inventory.Host` and :obj:`brigade.core.inventory.Group` objects as usual and then finally the ``transform_function`` is going to be called for each individual host one by one. + +.. note:: This was a very simple example but the ``transform_function`` can basically do anything you want/need. diff --git a/docs/howto/writing_a_custom_inventory.rst b/docs/howto/writing_a_custom_inventory.rst new file mode 100644 index 00000000..95739348 --- /dev/null +++ b/docs/howto/writing_a_custom_inventory.rst @@ -0,0 +1,48 @@ +Writing a custom inventory +========================== + +If you have your own backend with host information or you don't like the provided ones you can write your own custom inventory. Doing so is quite easy. A continuation you can find a very simple one with static data:: + + from builtins import super + + from brigade.core.inventory import Inventory + + + class MyInventory(Inventory): + + def __init__(self, **kwargs): + # code to get the data + hosts = { + "host1": { + "data1": "value1", + "data2": "value2". + "group": "my_group1", + }, + "host2": { + "data1": "value1", + "data2": "value2". + "group": "my_group1", + } + } + groups = { + "my_group1": { + "more_data1": "more_value1", + "more_data2": "more_value2", + } + } + + # passing the data to the parent class so the data is + # transformed into actual Host/Group objects + super().__init__(hosts, groups, **kwargs) + + +So if you want to make it dynamic everything you have to do is get the data yourself and organize it in a similar format to the one described in the example above. + +.. note:: it is not mandatory to use groups. Feel free to skip the attribute ``group`` and just pass and empty dict or ``None`` to ``super()``. + +Finally, to have brigade use it, you can do:: + + inv = MyInventory() + brigade = Brigade(inventory=inv) + +And that's it, you now have your own inventory plugin :) diff --git a/docs/ref/inventory/index.rst b/docs/ref/inventory/index.rst index 0005b8ea..5af16f3f 100644 --- a/docs/ref/inventory/index.rst +++ b/docs/ref/inventory/index.rst @@ -1,6 +1,9 @@ Inventory ========= -.. automodule:: brigade.plugins.inventory.simple - :members: - :undoc-members: +.. toctree:: + :maxdepth: 2 + :caption: Categories: + + simple + nsot diff --git a/docs/ref/inventory/nsot.rst b/docs/ref/inventory/nsot.rst new file mode 100644 index 00000000..8663d573 --- /dev/null +++ b/docs/ref/inventory/nsot.rst @@ -0,0 +1,6 @@ +NSOT +==== + +.. automodule:: brigade.plugins.inventory.nsot + :members: + :undoc-members: diff --git a/docs/ref/inventory/simple.rst b/docs/ref/inventory/simple.rst new file mode 100644 index 00000000..3a87e8f3 --- /dev/null +++ b/docs/ref/inventory/simple.rst @@ -0,0 +1,6 @@ +Simple +====== + +.. automodule:: brigade.plugins.inventory.simple + :members: + :undoc-members: diff --git a/docs/ref/tasks/connections.rst b/docs/ref/tasks/connections.rst new file mode 100644 index 00000000..efc9de64 --- /dev/null +++ b/docs/ref/tasks/connections.rst @@ -0,0 +1,6 @@ +Connections +=========== + +.. automodule:: brigade.plugins.tasks.connections + :members: + :undoc-members: diff --git a/docs/ref/tasks/index.rst b/docs/ref/tasks/index.rst index 55111c31..96558306 100644 --- a/docs/ref/tasks/index.rst +++ b/docs/ref/tasks/index.rst @@ -5,6 +5,7 @@ Tasks :maxdepth: 2 :caption: Categories: + connections commands data files diff --git a/requirements.txt b/requirements.txt index 13de20d1..62da29d3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ napalm netmiko paramiko future +requests diff --git a/tests/inventory_data/nsot/nsot.sh b/tests/inventory_data/nsot/nsot.sh new file mode 100644 index 00000000..5a8490c6 --- /dev/null +++ b/tests/inventory_data/nsot/nsot.sh @@ -0,0 +1,79 @@ +nsot sites add --name site1 + +nsot attributes add --site-id 1 --resource-name device --name os +nsot attributes add --site-id 1 --resource-name device --name host +nsot attributes add --site-id 1 --resource-name device --name user +nsot attributes add --site-id 1 --resource-name device --name password --allow-empty +nsot attributes add --site-id 1 --resource-name device --name port + +nsot devices add --site-id 1 --hostname rtr00-site1 +nsot devices update --site-id 1 --id 1 -a os=eos -a host=127.0.0.1 -a user=vagrant -a password=vagrant -a port=12443 +nsot devices add --site-id 1 --hostname rtr01-site1 +nsot devices update --site-id 1 --id 2 -a os=junos -a host=127.0.0.1 -a user=vagrant -a password="" -a port=12203 + + +nsot attributes add --site-id 1 --resource-name device --name asn +nsot attributes add --site-id 1 --resource-name device --name router_id + +nsot devices update --site-id 1 --id 1 -a asn=65001 -a router_id=10.1.1.1 +nsot devices update --site-id 1 --id 2 -a asn=65002 -a router_id=10.1.1.2 + +nsot attributes add --site-id 1 --resource-name network --name type +nsot networks add --site-id 1 --cidr 2001:db8:b33f::/64 -a type=loopbacks + +nsot attributes add --site-id 1 --resource-name interface --name link_type +nsot attributes add --site-id 1 --resource-name interface --name connects_to_device +nsot attributes add --site-id 1 --resource-name interface --name connects_to_iface + +nsot interfaces add --site-id 1 --device 1 --name lo0 --addresses 2001:db8:b33f::100/128 -a link_type=loopback -a connects_to_device=loopback -a connects_to_iface=lo0 +nsot interfaces add --site-id 1 --device 2 --name lo0 --addresses 2001:db8:b33f::101/128 -a link_type=loopback -a connects_to_device=loopback -a connects_to_iface=lo0 + +nsot networks add --site-id 1 --cidr 2001:db8:caf3::/64 -a type=ptp +nsot networks add --site-id 1 --cidr 2001:db8:caf3::/127 -a type=ptp +nsot networks add --site-id 1 --cidr 2001:db8:caf3::2/127 -a type=ptp + +nsot interfaces add --site-id 1 --device 1 --name et1 -a link_type=fabric -a connects_to_device=rtr01 -a connects_to_iface=ge-0/0/1 -c 2001:db8:caf3:: +nsot interfaces add --site-id 1 --device 1 --name et2 -a link_type=fabric -a connects_to_device=rtr01 -a connects_to_iface=ge-0/0/2 -c 2001:db8:caf3::2 + +nsot interfaces add --site-id 1 --device 2 --name ge-0/0/1 -a link_type=fabric -a connects_to_device=rtr00 -a connects_to_iface=et1 -c 2001:db8:caf3::1 +nsot interfaces add --site-id 1 --device 2 --name ge-0/0/2 -a link_type=fabric -a connects_to_device=rtr00 -a connects_to_iface=et2 -c 2001:db8:caf3::3 + +nsot sites add --name site2 + +nsot attributes add --site-id 2 --resource-name device --name os +nsot attributes add --site-id 2 --resource-name device --name host +nsot attributes add --site-id 2 --resource-name device --name user +nsot attributes add --site-id 2 --resource-name device --name password --allow-empty +nsot attributes add --site-id 2 --resource-name device --name port + +nsot devices add --site-id 2 --hostname rtr00-site2 +nsot devices update --site-id 2 --id 3 -a os=eos -a host=127.0.0.1 -a user=vagrant -a password=vagrant -a port=12443 +nsot devices add --site-id 2 --hostname rtr01-site2 +nsot devices update --site-id 2 --id 4 -a os=junos -a host=127.0.0.1 -a user=vagrant -a password="" -a port=12203 + + +nsot attributes add --site-id 2 --resource-name device --name asn +nsot attributes add --site-id 2 --resource-name device --name router_id + +nsot devices update --site-id 2 --id 3 -a asn=65001 -a router_id=10.1.1.1 +nsot devices update --site-id 2 --id 4 -a asn=65002 -a router_id=10.1.1.2 + +nsot attributes add --site-id 2 --resource-name network --name type +nsot networks add --site-id 2 --cidr 2001:db8:f4c3::/64 -a type=loopbacks + +nsot attributes add --site-id 2 --resource-name interface --name link_type +nsot attributes add --site-id 2 --resource-name interface --name connects_to_device +nsot attributes add --site-id 2 --resource-name interface --name connects_to_iface + +nsot interfaces add --site-id 2 --device 3 --name lo0 --addresses 2001:db8:f4c3::100/128 -a link_type=loopback -a connects_to_device=loopback -a connects_to_iface=lo0 +nsot interfaces add --site-id 2 --device 4 --name lo0 --addresses 2001:db8:f4c3::101/128 -a link_type=loopback -a connects_to_device=loopback -a connects_to_iface=lo0 + +nsot networks add --site-id 2 --cidr 2001:db8:dead::/64 -a type=ptp +nsot networks add --site-id 2 --cidr 2001:db8:dead::/127 -a type=ptp +nsot networks add --site-id 2 --cidr 2001:db8:dead::2/127 -a type=ptp + +nsot interfaces add --site-id 2 --device 3 --name et1 -a link_type=fabric -a connects_to_device=rtr01 -a connects_to_iface=ge-0/0/1 -c 2001:db8:dead:: +nsot interfaces add --site-id 2 --device 3 --name et2 -a link_type=fabric -a connects_to_device=rtr01 -a connects_to_iface=ge-0/0/2 -c 2001:db8:dead::2 + +nsot interfaces add --site-id 2 --device 4 --name ge-0/0/1 -a link_type=fabric -a connects_to_device=rtr00 -a connects_to_iface=et1 -c 2001:db8:dead::1 +nsot interfaces add --site-id 2 --device 4 --name ge-0/0/2 -a link_type=fabric -a connects_to_device=rtr00 -a connects_to_iface=et2 -c 2001:db8:dead::3 diff --git a/tests/inventory_data/nsot/nsot.sqlite3 b/tests/inventory_data/nsot/nsot.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..a1217f69ee7103d2e7c960baf0f341ea4340190d GIT binary patch literal 220160 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 Date: Sun, 17 Dec 2017 01:46:23 -0800 Subject: [PATCH 27/67] Add netmiko_send_command task (#51) * Migrating test_netmiko_run to test_netmiko_send_command * Add send_command_timing to tests * Fixing unit test * Add connection setup * Fix unit tests --- brigade/plugins/tasks/networking/__init__.py | 4 +- .../plugins/tasks/networking/netmiko_run.py | 41 ------------------- .../tasks/networking/netmiko_send_command.py | 22 ++++++++++ docs/conf.py | 4 +- requirements.txt | 4 +- .../tasks/networking/test_netmiko_run.py | 12 ------ .../networking/test_netmiko_send_command.py | 19 +++++++++ 7 files changed, 48 insertions(+), 58 deletions(-) delete mode 100644 brigade/plugins/tasks/networking/netmiko_run.py create mode 100644 brigade/plugins/tasks/networking/netmiko_send_command.py delete mode 100644 tests/plugins/tasks/networking/test_netmiko_run.py create mode 100644 tests/plugins/tasks/networking/test_netmiko_send_command.py diff --git a/brigade/plugins/tasks/networking/__init__.py b/brigade/plugins/tasks/networking/__init__.py index 0dd80984..2b62194e 100644 --- a/brigade/plugins/tasks/networking/__init__.py +++ b/brigade/plugins/tasks/networking/__init__.py @@ -1,13 +1,13 @@ from .napalm_cli import napalm_cli from .napalm_configure import napalm_configure from .napalm_get import napalm_get -from .netmiko_run import netmiko_run +from .netmiko_send_command import netmiko_send_command from .tcp_ping import tcp_ping __all__ = ( "napalm_cli", "napalm_configure", "napalm_get", - "netmiko_run", + "netmiko_send_command", "tcp_ping", ) diff --git a/brigade/plugins/tasks/networking/netmiko_run.py b/brigade/plugins/tasks/networking/netmiko_run.py deleted file mode 100644 index 9a6315a4..00000000 --- a/brigade/plugins/tasks/networking/netmiko_run.py +++ /dev/null @@ -1,41 +0,0 @@ -from brigade.core.task import Result - -from netmiko import ConnectHandler - -napalm_to_netmiko_map = { - 'ios': 'cisco_ios', - 'nxos': 'cisco_nxos', - 'eos': 'arista_eos', - 'junos': 'juniper_junos', - 'iosxr': 'cisco_iosxr' -} - - -def netmiko_run(task, method, netmiko_dict=None, **kwargs): - """ - Execute any Netmiko method from connection class (BaseConnection class and children). - - Arguments: - method(str): Netmiko method to use - netmiko_dict (dict, optional): Additional arguments to pass to Netmiko ConnectHandler, \ - defaults to None - - Returns: - :obj:`brigade.core.task.Result`: - * result (``dict``): dictionary with the result of the getter - """ - parameters = { - "ip": task.host.host, - "username": task.host.username, - "password": task.host.password, - "port": task.host.ssh_port, - } - parameters.update(netmiko_dict or {}) - device_type = task.host.nos - # Convert to netmiko device_type format (if napalm format is used) - parameters['device_type'] = napalm_to_netmiko_map.get(device_type, device_type) - - with ConnectHandler(**parameters) as net_connect: - netmiko_method = getattr(net_connect, method) - result = netmiko_method(**kwargs) - return Result(host=task.host, result=result) diff --git a/brigade/plugins/tasks/networking/netmiko_send_command.py b/brigade/plugins/tasks/networking/netmiko_send_command.py new file mode 100644 index 00000000..7bcb7328 --- /dev/null +++ b/brigade/plugins/tasks/networking/netmiko_send_command.py @@ -0,0 +1,22 @@ +from brigade.core.task import Result + + +def netmiko_send_command(task, command_string, use_timing=False, **kwargs): + """ + Execute Netmiko send_command method (or send_command_timing) + + Arguments: + command_string(str): Command to execute on the remote network device. + use_timing(bool, optional): Set to True to switch to send_command_timing method. + kwargs (dict, optional): Additional arguments to pass to send_command method. + + Returns: + :obj:`brigade.core.task.Result`: + * result (``dict``): dictionary with the result of the getter + """ + net_connect = task.host.get_connection("netmiko") + if use_timing: + result = net_connect.send_command_timing(command_string, **kwargs) + else: + result = net_connect.send_command(command_string, **kwargs) + return Result(host=task.host, result=result) diff --git a/docs/conf.py b/docs/conf.py index e7b67dbb..ba2321d7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -19,7 +19,9 @@ # import os import sys -from jinja2 import Environment, FileSystemLoader + +from jinja2 import Environment +from jinja2 import FileSystemLoader sys.path.insert(0, os.path.abspath('../')) diff --git a/requirements.txt b/requirements.txt index 62da29d3..235d09d0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ pyyaml jinja2 -napalm -netmiko +napalm>=2.2.0 +netmiko>=2.0.0 paramiko future requests diff --git a/tests/plugins/tasks/networking/test_netmiko_run.py b/tests/plugins/tasks/networking/test_netmiko_run.py deleted file mode 100644 index 256f867c..00000000 --- a/tests/plugins/tasks/networking/test_netmiko_run.py +++ /dev/null @@ -1,12 +0,0 @@ -from brigade.plugins.tasks import networking - - -class Test(object): - - def test_netmiko_run(self, brigade): - result = brigade.filter(name="dev4.group_2").run(networking.netmiko_run, - method="send_command", - command_string="hostname") - assert result - for h, r in result.items(): - assert h == r.result.strip() diff --git a/tests/plugins/tasks/networking/test_netmiko_send_command.py b/tests/plugins/tasks/networking/test_netmiko_send_command.py new file mode 100644 index 00000000..e3fd69ca --- /dev/null +++ b/tests/plugins/tasks/networking/test_netmiko_send_command.py @@ -0,0 +1,19 @@ +from brigade.plugins.tasks import connections, networking + + +class Test(object): + + def test_netmiko_send_command(self, brigade): + brigade.filter(name="dev4.group_2").run(task=connections.netmiko_connection) + result = brigade.filter(name="dev4.group_2").run(networking.netmiko_send_command, + command_string="hostname") + assert result + for h, r in result.items(): + assert h == r.result.strip() + + result = brigade.filter(name="dev4.group_2").run(networking.netmiko_send_command, + command_string="hostname", + use_timing=True) + assert result + for h, r in result.items(): + assert h == r.result.strip() From 1dad2df305784acfe858b1db933638c76b9b47fd Mon Sep 17 00:00:00 2001 From: David Barroso Date: Sun, 17 Dec 2017 10:59:16 +0100 Subject: [PATCH 28/67] connect automatically (#50) * added a reference to brigade to the inventory and to the hosts and groups * added auto connect back * use properties in the host as well for consistency * removed unnecessary attributes * allow customizing the connections that are available to the tasks * handle properly brigade object in the inventory * Added documentation --- brigade/core/__init__.py | 13 +++- brigade/core/helpers/__init__.py | 3 +- brigade/core/inventory.py | 64 +++++++++++++++---- brigade/plugins/tasks/connections/__init__.py | 7 ++ .../tasks/connections/napalm_connection.py | 6 +- docs/howto/writing_a_connection_task.rst | 24 +++++++ tests/conftest.py | 4 +- 7 files changed, 101 insertions(+), 20 deletions(-) create mode 100644 docs/howto/writing_a_connection_task.rst diff --git a/brigade/core/__init__.py b/brigade/core/__init__.py index 7e724a14..d92400e6 100644 --- a/brigade/core/__init__.py +++ b/brigade/core/__init__.py @@ -5,6 +5,7 @@ from brigade.core.configuration import Config from brigade.core.task import AggregatedResult, Task +from brigade.plugins.tasks import connections if sys.version_info.major == 2: @@ -45,16 +46,20 @@ class Brigade(object): dry_run(``bool``): Whether if we are testing the changes or not config (:obj:`brigade.core.configuration.Config`): Configuration object config_file (``str``): Path to Yaml configuration file + available_connections (``dict``): dict of connection types that will be made available. + Defaults to :obj:`brigade.plugins.tasks.connections.available_connections` Attributes: inventory (:obj:`brigade.core.inventory.Inventory`): Inventory to work with dry_run(``bool``): Whether if we are testing the changes or not config (:obj:`brigade.core.configuration.Config`): Configuration parameters + available_connections (``dict``): dict of connection types are available """ - def __init__(self, inventory, dry_run, - config=None, config_file=None): + def __init__(self, inventory, dry_run, config=None, config_file=None, + available_connections=None): self.inventory = inventory + self.inventory.brigade = self self.dry_run = dry_run if config_file: @@ -68,6 +73,10 @@ def __init__(self, inventory, dry_run, level=logging.ERROR, format=format, ) + if available_connections is not None: + self.available_connections = available_connections + else: + self.available_connections = connections.available_connections def filter(self, **kwargs): """ diff --git a/brigade/core/helpers/__init__.py b/brigade/core/helpers/__init__.py index 44738ef4..652792b4 100644 --- a/brigade/core/helpers/__init__.py +++ b/brigade/core/helpers/__init__.py @@ -8,6 +8,5 @@ def merge_two_dicts(x, y): def format_string(text, task, **kwargs): - merged = merge_two_dicts(task.host.items(), task.brigade.inventory.data) return text.format(host=task.host, - **merge_two_dicts(merged, kwargs)) + **merge_two_dicts(task.host.items(), kwargs)) diff --git a/brigade/core/inventory.py b/brigade/core/inventory.py index a32c4171..52c5caf9 100644 --- a/brigade/core/inventory.py +++ b/brigade/core/inventory.py @@ -14,12 +14,14 @@ class Host(object): Arguments: name (str): Name of the host group (:obj:`Group`, optional): Group the host belongs to + brigade (:obj:`brigade.core.Brigade`): Reference to the parent brigade object **kwargs: Host data Attributes: name (str): Name of the host group (:obj:`Group`): Group the host belongs to data (dict): data about the device + connections (``dict``): Already established connections Note: @@ -59,7 +61,8 @@ class Host(object): * ``my_host.group.group.data["domain"]`` will return ``acme.com`` """ - def __init__(self, name, group=None, **kwargs): + def __init__(self, name, group=None, brigade=None, **kwargs): + self.brigade = brigade self.name = name self.group = group self.data = {} @@ -135,6 +138,18 @@ def items(self): d = {} return helpers.merge_two_dicts(d, self.data) + @property + def brigade(self): + """Reference to the parent :obj:`brigade.core.Brigade` object""" + return self._brigade + + @brigade.setter + def brigade(self, value): + # If it's already set we don't want to set it again + # because we may lose valuable information + if not getattr(self, "_brigade", None): + self._brigade = value + @property def host(self): """String used to connect to the device. Either ``brigade_host`` or ``self.name``""" @@ -175,7 +190,11 @@ def nos(self): def get_connection(self, connection): """ - This function will return an already established connection + The function of this method is twofold: + + 1. If an existing connection is already established for the given type return it + 2. If non exists, establish a new connection of that type with default parameters + and return it Raises: AttributeError: if it's unknown how to establish a connection for the given @@ -189,11 +208,16 @@ def get_connection(self, connection): An already established connection of type ``connection`` """ if connection not in self.connections: - msg = ( - "Couldn't find an established connection for '{c}'. " - "Did you call '{c}_connection'?" - ).format(c=connection) - raise AttributeError(msg) + try: + conn_task = self.brigade.available_connections[connection] + except KeyError: + raise AttributeError("not sure how to establish a connection for {}".format( + connection)) + # We use `filter(name=self.name)` to call the connection task for only + # the given host. We also have to set `num_workers=1` because chances are + # we are already inside a thread + # Task should establish a connection and populate self.connection[connection] + self.brigade.filter(name=self.name).run(conn_task, num_workers=1) return self.connections[connection] @@ -220,14 +244,14 @@ class Inventory(object): groups (dict): keys are group names and the values are :obj:`Group`. """ - def __init__(self, hosts, groups=None, data=None, host_data=None, transform_function=None): - self.data = data or {} + def __init__(self, hosts, groups=None, transform_function=None, brigade=None): + self._brigade = brigade groups = groups or {} self.groups = {} for n, g in groups.items(): if isinstance(g, dict): - g = Group(name=n, **g) + g = Group(name=n, brigade=brigade, **g) self.groups[n] = g for g in self.groups.values(): @@ -237,7 +261,7 @@ def __init__(self, hosts, groups=None, data=None, host_data=None, transform_func self.hosts = {} for n, h in hosts.items(): if isinstance(h, dict): - h = Host(name=n, **h) + h = Host(name=n, brigade=brigade, **h) if transform_function: transform_function(h) @@ -275,7 +299,23 @@ def filter(self, filter_func=None, **kwargs): else: filtered = {n: h for n, h in self.hosts.items() if all(h.get(k) == v for k, v in kwargs.items())} - return Inventory(hosts=filtered, groups=self.groups, data=self.data) + return Inventory(hosts=filtered, groups=self.groups, brigade=self.brigade) def __len__(self): return self.hosts.__len__() + + @property + def brigade(self): + """Reference to the parent :obj:`brigade.core.Brigade` object""" + return self._brigade + + @brigade.setter + def brigade(self, value): + if not getattr(self, "_brigade", None): + self._brigade = value + + for h in self.hosts.values(): + h.brigade = value + + for g in self.groups.values(): + g.brigade = value diff --git a/brigade/plugins/tasks/connections/__init__.py b/brigade/plugins/tasks/connections/__init__.py index 6bca8d87..d504f6e7 100644 --- a/brigade/plugins/tasks/connections/__init__.py +++ b/brigade/plugins/tasks/connections/__init__.py @@ -2,6 +2,13 @@ from .netmiko_connection import netmiko_connection from .paramiko_connection import paramiko_connection + +available_connections = { + "napalm": napalm_connection, + "netmiko": netmiko_connection, + "paramiko": paramiko_connection, +} + __all__ = ( "napalm_connection", "netmiko_connection", diff --git a/brigade/plugins/tasks/connections/napalm_connection.py b/brigade/plugins/tasks/connections/napalm_connection.py index 22ee8821..607f5533 100644 --- a/brigade/plugins/tasks/connections/napalm_connection.py +++ b/brigade/plugins/tasks/connections/napalm_connection.py @@ -9,6 +9,10 @@ def napalm_connection(task=None, timeout=60, optional_args=None): Arguments: timeout (int, optional): defaults to 60 optional_args (dict, optional): defaults to ``{"port": task.host["network_api_port"]}`` + + Inventory: + napalm_options: maps directly to ``optional_args`` when establishing the connection + network_api_port: maps to ``optional_args["port"]`` """ host = task.host @@ -17,7 +21,7 @@ def napalm_connection(task=None, timeout=60, optional_args=None): "username": host.username, "password": host.password, "timeout": timeout, - "optional_args": optional_args or {}, + "optional_args": optional_args or host.get("napalm_options", {}), } if "port" not in parameters["optional_args"] and host.network_api_port: parameters["optional_args"]["port"] = host.network_api_port diff --git a/docs/howto/writing_a_connection_task.rst b/docs/howto/writing_a_connection_task.rst new file mode 100644 index 00000000..dad98a48 --- /dev/null +++ b/docs/howto/writing_a_connection_task.rst @@ -0,0 +1,24 @@ +Writing a connection task +######################### + +Connection tasks are tasks that establish a connection with a device to provide some sort of reusable mechanism to interact with it. You can find some examples of connections tasks in the :doc:`../ref/tasks/connections` section. + +Writing a connection task is no different from writing a regular task. The only difference is that the task will have to establish the connection and assign it to the device. + +A continuation you can see a simplified version of the ``paramiko_connection`` connection task as an example:: + + def paramiko_connection(task=None): + host = task.host + + client = paramiko.SSHClient() + + parameters = { + "hostname": host.host, + "username": host.username, + "password": host.password, + "port": host.ssh_port, + } + client.connect(**parameters) + host.connections["paramiko"] = client + +Note the last line where the connection is assigned to the host. Subsequent tasks will be able to retrieve this connection by host calling ``host.get_connection("paramiko")`` diff --git a/tests/conftest.py b/tests/conftest.py index 0b47b19a..abe65897 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,7 +4,6 @@ from brigade.core import Brigade from brigade.plugins.inventory.simple import SimpleInventory -from brigade.plugins.tasks.connections import paramiko_connection import pytest @@ -18,7 +17,7 @@ @pytest.fixture(scope="session", autouse=True) -def a_containers(request): +def containers(request): """Start/Stop containers needed for the tests.""" def fin(): logging.info("Stopping containers") @@ -45,5 +44,4 @@ def brigade(request): "{}/inventory_data/groups.yaml".format(dir_path)), dry_run=True, ) - brigade.run(paramiko_connection) return brigade From c06ddea0c5c79e734e3b6f857af5165a17e7c798 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Mon, 18 Dec 2017 08:27:53 +0100 Subject: [PATCH 29/67] bugfixes on napalm_get task (#54) --- brigade/plugins/tasks/networking/napalm_get.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/brigade/plugins/tasks/networking/napalm_get.py b/brigade/plugins/tasks/networking/napalm_get.py index ef4ff065..f90564d9 100644 --- a/brigade/plugins/tasks/networking/napalm_get.py +++ b/brigade/plugins/tasks/networking/napalm_get.py @@ -15,13 +15,12 @@ def napalm_get(task, getters): """ device = task.host.get_connection("napalm") - if not isinstance(getters, list): + if isinstance(getters, str): getters = [getters] result = {} for g in getters: - if not g.startswith("get_"): - getter = "get_{}".format(g) + getter = g if g.startswith("get_") else "get_{}".format(g) method = getattr(device, getter) result[g] = method() return Result(host=task.host, result=result) From 81f42d27f074a25cd19b3dfbe756b59a4e228d9e Mon Sep 17 00:00:00 2001 From: David Barroso Date: Mon, 18 Dec 2017 08:41:46 +0100 Subject: [PATCH 30/67] making Config configurable programmatically (#55) --- brigade/core/configuration.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/brigade/core/configuration.py b/brigade/core/configuration.py index c0b38c04..61d8c042 100644 --- a/brigade/core/configuration.py +++ b/brigade/core/configuration.py @@ -41,7 +41,7 @@ class Config: config_file(``str``): Yaml configuration file. """ - def __init__(self, config_file=None): + def __init__(self, config_file=None, **kwargs): if config_file: with open(config_file, 'r') as f: @@ -51,6 +51,9 @@ def __init__(self, config_file=None): self._assign_properties(c) + for k, v in kwargs.items(): + setattr(self, k, v) + def _assign_properties(self, c): for p in CONF: From b2cf0b66dabee7630808aee89dffd79fac64e8f8 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Mon, 18 Dec 2017 09:16:58 +0100 Subject: [PATCH 31/67] added task.run to help executing subtasks (#56) --- brigade/core/task.py | 19 +++++++++++++++++++ tests/core/test_tasks.py | 22 ++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/core/test_tasks.py diff --git a/brigade/core/task.py b/brigade/core/task.py index d564b78b..eee86b06 100644 --- a/brigade/core/task.py +++ b/brigade/core/task.py @@ -38,6 +38,25 @@ def _start(self, host, brigade, dry_run): self.dry_run = dry_run return self.task(self, **self.params) + def run(self, task, **kwargs): + """ + This is a utility method to call a task from within a task. For instance: + + def grouped_tasks(task): + task.run(my_first_task) + task.run(my_second_task) + + brigade.run(grouped_tasks) + + This method will ensure the subtask is run only for the host in the current thread. + """ + if not self.host or not self.brigade: + msg = ("You have to call this after setting host and brigade attributes. ", + "You probably called this from outside a nested task") + raise Exception(msg) + aggr = self.brigade.filter(name=self.host.name).run(task, num_workers=1, **kwargs) + return aggr[self.host.name] + class Result(object): """ diff --git a/tests/core/test_tasks.py b/tests/core/test_tasks.py new file mode 100644 index 00000000..93a942ea --- /dev/null +++ b/tests/core/test_tasks.py @@ -0,0 +1,22 @@ +from brigade.plugins.tasks import commands + + +def sub_task(task): + return task.run(commands.command, + command="echo {}".format(task.host)) + + +class Test(object): + + def test_task(self, brigade): + result = brigade.run(commands.command, + command="echo hi") + assert result + for h, r in result.items(): + assert r.stdout.strip() == "hi" + + def test_sub_task(self, brigade): + result = brigade.run(sub_task) + assert result + for h, r in result.items(): + assert h == r.stdout.strip() From dcecaf2312e4d26f509c7a099f3a9bc293079a6a Mon Sep 17 00:00:00 2001 From: David Barroso Date: Tue, 19 Dec 2017 09:47:12 +0100 Subject: [PATCH 32/67] rename for clarity --- {demo => examples}/Vagrantfile | 0 {demo => examples}/configure.py | 0 {demo => examples}/extra_data/switch00.bma/interfaces.yaml | 0 {demo => examples}/extra_data/switch00.cmh/interfaces.yaml | 0 {demo => examples}/extra_data/switch01.bma/interfaces.yaml | 0 {demo => examples}/extra_data/switch01.cmh/interfaces.yaml | 0 {demo => examples}/get_facts_grouping.py | 0 {demo => examples}/get_facts_simple.py | 0 {demo => examples}/groups.yaml | 0 {demo => examples}/hosts.yaml | 0 {demo => examples}/requirements.txt | 0 {demo => examples}/templates/base/eos/base.j2 | 0 {demo => examples}/templates/base/junos/base.j2 | 0 {demo => examples}/templates/interfaces/eos/interfaces.j2 | 0 {demo => examples}/templates/interfaces/junos/interfaces.j2 | 0 15 files changed, 0 insertions(+), 0 deletions(-) rename {demo => examples}/Vagrantfile (100%) rename {demo => examples}/configure.py (100%) rename {demo => examples}/extra_data/switch00.bma/interfaces.yaml (100%) rename {demo => examples}/extra_data/switch00.cmh/interfaces.yaml (100%) rename {demo => examples}/extra_data/switch01.bma/interfaces.yaml (100%) rename {demo => examples}/extra_data/switch01.cmh/interfaces.yaml (100%) rename {demo => examples}/get_facts_grouping.py (100%) rename {demo => examples}/get_facts_simple.py (100%) rename {demo => examples}/groups.yaml (100%) rename {demo => examples}/hosts.yaml (100%) rename {demo => examples}/requirements.txt (100%) rename {demo => examples}/templates/base/eos/base.j2 (100%) rename {demo => examples}/templates/base/junos/base.j2 (100%) rename {demo => examples}/templates/interfaces/eos/interfaces.j2 (100%) rename {demo => examples}/templates/interfaces/junos/interfaces.j2 (100%) diff --git a/demo/Vagrantfile b/examples/Vagrantfile similarity index 100% rename from demo/Vagrantfile rename to examples/Vagrantfile diff --git a/demo/configure.py b/examples/configure.py similarity index 100% rename from demo/configure.py rename to examples/configure.py diff --git a/demo/extra_data/switch00.bma/interfaces.yaml b/examples/extra_data/switch00.bma/interfaces.yaml similarity index 100% rename from demo/extra_data/switch00.bma/interfaces.yaml rename to examples/extra_data/switch00.bma/interfaces.yaml diff --git a/demo/extra_data/switch00.cmh/interfaces.yaml b/examples/extra_data/switch00.cmh/interfaces.yaml similarity index 100% rename from demo/extra_data/switch00.cmh/interfaces.yaml rename to examples/extra_data/switch00.cmh/interfaces.yaml diff --git a/demo/extra_data/switch01.bma/interfaces.yaml b/examples/extra_data/switch01.bma/interfaces.yaml similarity index 100% rename from demo/extra_data/switch01.bma/interfaces.yaml rename to examples/extra_data/switch01.bma/interfaces.yaml diff --git a/demo/extra_data/switch01.cmh/interfaces.yaml b/examples/extra_data/switch01.cmh/interfaces.yaml similarity index 100% rename from demo/extra_data/switch01.cmh/interfaces.yaml rename to examples/extra_data/switch01.cmh/interfaces.yaml diff --git a/demo/get_facts_grouping.py b/examples/get_facts_grouping.py similarity index 100% rename from demo/get_facts_grouping.py rename to examples/get_facts_grouping.py diff --git a/demo/get_facts_simple.py b/examples/get_facts_simple.py similarity index 100% rename from demo/get_facts_simple.py rename to examples/get_facts_simple.py diff --git a/demo/groups.yaml b/examples/groups.yaml similarity index 100% rename from demo/groups.yaml rename to examples/groups.yaml diff --git a/demo/hosts.yaml b/examples/hosts.yaml similarity index 100% rename from demo/hosts.yaml rename to examples/hosts.yaml diff --git a/demo/requirements.txt b/examples/requirements.txt similarity index 100% rename from demo/requirements.txt rename to examples/requirements.txt diff --git a/demo/templates/base/eos/base.j2 b/examples/templates/base/eos/base.j2 similarity index 100% rename from demo/templates/base/eos/base.j2 rename to examples/templates/base/eos/base.j2 diff --git a/demo/templates/base/junos/base.j2 b/examples/templates/base/junos/base.j2 similarity index 100% rename from demo/templates/base/junos/base.j2 rename to examples/templates/base/junos/base.j2 diff --git a/demo/templates/interfaces/eos/interfaces.j2 b/examples/templates/interfaces/eos/interfaces.j2 similarity index 100% rename from demo/templates/interfaces/eos/interfaces.j2 rename to examples/templates/interfaces/eos/interfaces.j2 diff --git a/demo/templates/interfaces/junos/interfaces.j2 b/examples/templates/interfaces/junos/interfaces.j2 similarity index 100% rename from demo/templates/interfaces/junos/interfaces.j2 rename to examples/templates/interfaces/junos/interfaces.j2 From 3fb9a8f8f0245006a1055212d6bf3d898642dfec Mon Sep 17 00:00:00 2001 From: David Barroso Date: Thu, 21 Dec 2017 12:39:54 +0100 Subject: [PATCH 33/67] Update references to examples --- README.md | 2 +- docs/howto/basic-napalm-getters.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f1114d2e..72344946 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ Brigade ======= -See [docs](https://brigade.readthedocs.io) and [demo/get_facts_simple.py](demo/get_facts_simple.py), [demo/get_facts_role.py](demo/get_facts_role.py), and [demo/configure.py](demo/configure.py). +See [docs](https://brigade.readthedocs.io). diff --git a/docs/howto/basic-napalm-getters.rst b/docs/howto/basic-napalm-getters.rst index dac30018..d72e0c6f 100644 --- a/docs/howto/basic-napalm-getters.rst +++ b/docs/howto/basic-napalm-getters.rst @@ -8,13 +8,13 @@ Let's start by seeing how to work with the inventory. Let's assume the following * Hosts file: -.. literalinclude:: ../../demo/hosts.yaml +.. literalinclude:: ../../examples/hosts.yaml :name: hosts.yaml :language: yaml * Groups file: -.. literalinclude:: ../../demo/groups.yaml +.. literalinclude:: ../../examples/groups.yaml :name: groups.yaml :language: yaml From 92fed6f0c47a01eb5be9d6d0f462578af0bbe128 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Thu, 21 Dec 2017 13:41:03 +0100 Subject: [PATCH 34/67] allow passing dry_run to individual tasks --- brigade/core/__init__.py | 19 +++++++------- brigade/core/task.py | 2 +- tests/inventory_data/nsot/nsot.sqlite3 | Bin 220160 -> 228352 bytes tests/plugins/tasks/files/test_sftp.py | 24 +++++++++--------- .../tasks/networking/test_napalm_configure.py | 10 +++++--- 5 files changed, 29 insertions(+), 26 deletions(-) diff --git a/brigade/core/__init__.py b/brigade/core/__init__.py index d92400e6..2a2dfbfc 100644 --- a/brigade/core/__init__.py +++ b/brigade/core/__init__.py @@ -89,13 +89,13 @@ def filter(self, **kwargs): b.inventory = self.inventory.filter(**kwargs) return b - def _run_serial(self, task, **kwargs): + def _run_serial(self, task, dry_run, **kwargs): t = Task(task, **kwargs) result = AggregatedResult() for host in self.inventory.hosts.values(): try: logger.debug("{}: running task {}".format(host.name, t)) - r = t._start(host=host, brigade=self, dry_run=self.dry_run) + r = t._start(host=host, brigade=self, dry_run=dry_run) result[host.name] = r except Exception as e: logger.error("{}: {}".format(host, e)) @@ -103,11 +103,11 @@ def _run_serial(self, task, **kwargs): result.tracebacks[host.name] = traceback.format_exc() return result - def _run_parallel(self, task, num_workers, **kwargs): + def _run_parallel(self, task, num_workers, dry_run, **kwargs): result = AggregatedResult() pool = Pool(processes=num_workers) - result_pool = [pool.apply_async(run_task, args=(h, self, Task(task, **kwargs))) + result_pool = [pool.apply_async(run_task, args=(h, self, dry_run, Task(task, **kwargs))) for h in self.inventory.hosts.values()] pool.close() pool.join() @@ -121,7 +121,7 @@ def _run_parallel(self, task, num_workers, **kwargs): result[host] = res return result - def run(self, task, num_workers=None, **kwargs): + def run(self, task, num_workers=None, dry_run=None, **kwargs): """ Run task over all the hosts in the inventory. @@ -129,6 +129,7 @@ def run(self, task, num_workers=None, **kwargs): task (``callable``): function or callable that will be run against each device in the inventory num_workers(``int``): Override for how many hosts to run in paralell for this task + dry_run(``bool``): Whether if we are testing the changes or not **kwargs: additional argument to pass to ``task`` when calling it Raises: @@ -141,19 +142,19 @@ def run(self, task, num_workers=None, **kwargs): num_workers = num_workers or self.config.num_workers if num_workers == 1: - result = self._run_serial(task, **kwargs) + result = self._run_serial(task, dry_run, **kwargs) else: - result = self._run_parallel(task, num_workers, **kwargs) + result = self._run_parallel(task, num_workers, dry_run, **kwargs) if self.config.raise_on_error: result.raise_on_error() return result -def run_task(host, brigade, task): +def run_task(host, brigade, dry_run, task): try: logger.debug("{}: running task {}".format(host.name, task)) - r = task._start(host=host, brigade=brigade, dry_run=brigade.dry_run) + r = task._start(host=host, brigade=brigade, dry_run=dry_run) return host.name, r, None, None except Exception as e: logger.error("{}: {}".format(host, e)) diff --git a/brigade/core/task.py b/brigade/core/task.py index eee86b06..72a1e694 100644 --- a/brigade/core/task.py +++ b/brigade/core/task.py @@ -35,7 +35,7 @@ def __repr__(self): def _start(self, host, brigade, dry_run): self.host = host self.brigade = brigade - self.dry_run = dry_run + self.dry_run = dry_run if dry_run is not None else brigade.dry_run return self.task(self, **self.params) def run(self, task, **kwargs): diff --git a/tests/inventory_data/nsot/nsot.sqlite3 b/tests/inventory_data/nsot/nsot.sqlite3 index a1217f69ee7103d2e7c960baf0f341ea4340190d..5fd8479baee4161f562baafe6f0233a43985d474 100644 GIT binary patch delta 3206 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 cX5QaOMZ6p!rx9 Date: Thu, 21 Dec 2017 14:05:31 +0100 Subject: [PATCH 35/67] added write task --- brigade/plugins/tasks/files/__init__.py | 2 + brigade/plugins/tasks/files/write.py | 48 +++++++ tests/plugins/tasks/files/test_write.py | 158 ++++++++++++++++++++++++ 3 files changed, 208 insertions(+) create mode 100644 brigade/plugins/tasks/files/write.py create mode 100644 tests/plugins/tasks/files/test_write.py diff --git a/brigade/plugins/tasks/files/__init__.py b/brigade/plugins/tasks/files/__init__.py index 1a430fa2..5c393eef 100644 --- a/brigade/plugins/tasks/files/__init__.py +++ b/brigade/plugins/tasks/files/__init__.py @@ -1,6 +1,8 @@ from .sftp import sftp +from .write import write __all__ = ( "sftp", + "write", ) diff --git a/brigade/plugins/tasks/files/write.py b/brigade/plugins/tasks/files/write.py new file mode 100644 index 00000000..6009e28a --- /dev/null +++ b/brigade/plugins/tasks/files/write.py @@ -0,0 +1,48 @@ +import difflib +import os + +from brigade.core.task import Result + + +def read_file(file): + if not os.path.exists(file): + return [] + with open(file, "r") as f: + return f.read().splitlines() + + +def generate_diff(filename, content, append): + original = read_file(filename) + if append: + c = list(original) + c.extend(content.splitlines()) + content = c + else: + content = content.splitlines() + + diff = difflib.unified_diff(original, content, fromfile=filename, tofile="new") + + return "\n".join(diff) + + +def write(task, filename, content, append=False): + """ + Write contents to a file (locally) + + Arguments: + filename (``str``): file you want to write into + conteint (``str``): content you want to write + append (``bool``): whether you want to replace the contents or append to it + + Returns: + * changed (``bool``): + * diff (``str``): unified diff + """ + diff = generate_diff(filename, content, append) + + if not task.dry_run: + mode = "a+" if append else "w+" + with open(filename, mode=mode) as f: + f.write(content) + + return Result(host=task.host, diff=diff, changed=bool(diff)) diff --git a/tests/plugins/tasks/files/test_write.py b/tests/plugins/tasks/files/test_write.py new file mode 100644 index 00000000..79584369 --- /dev/null +++ b/tests/plugins/tasks/files/test_write.py @@ -0,0 +1,158 @@ +import os +import uuid + +from brigade.plugins.tasks import files + + +content_a = """ +BLAH +BLEH +BLIH +BLOH +BLUH +""" + +content_b = """ +BLAH +BLOH +BLUH BLUH +BLIH +""" + + +diff_new = """--- /tmp/brigade-write/dev3.group_2-f66d9331-3eeb-4912-98b9-37f55ac48deb + ++++ new + +@@ -0,0 +1,6 @@ + ++ ++BLAH ++BLEH ++BLIH ++BLOH ++BLUH""" + +diff_overwrite = """--- /tmp/brigade-write/dev4.group_2-e63969eb-2261-4200-8913-196a12f4d791 + ++++ new + +@@ -1,6 +1,5 @@ + + + BLAH +-BLEH ++BLOH ++BLUH BLUH + BLIH +-BLOH +-BLUH""" # noqa + + +diff_append = """--- /tmp/brigade-write/dev4.group_2-36ea350d-6623-4098-a961-fc143504eb42 + ++++ new + +@@ -4,3 +4,8 @@ + + BLIH + BLOH + BLUH ++ ++BLAH ++BLOH ++BLUH BLUH ++BLIH""" # noqa + + +BASEPATH = "/tmp/brigade-write" +if not os.path.exists(BASEPATH): + os.makedirs(BASEPATH) + + +def _test_write(task): + filename = "{}/{}-{}".format(BASEPATH, task.host, str(uuid.uuid4())) + r = task.run(files.write, + dry_run=True, + filename=filename, + content=content_a) + + assert r.diff.splitlines()[1:] == diff_new.splitlines()[1:] + assert r.changed + + r = task.run(files.write, + dry_run=False, + filename=filename, + content=content_a) + + assert r.diff.splitlines()[1:] == diff_new.splitlines()[1:] + assert r.changed + + r = task.run(files.write, + dry_run=False, + filename=filename, + content=content_a) + + assert not r.diff + assert not r.changed + + +def _test_overwrite(task): + filename = "{}/{}-{}".format(BASEPATH, task.host, str(uuid.uuid4())) + + r = task.run(files.write, + dry_run=False, + filename=filename, + content=content_a) + + assert r.diff.splitlines()[1:] == diff_new.splitlines()[1:] + assert r.changed + + r = task.run(files.write, + dry_run=False, + filename=filename, + content=content_b) + + assert r.diff.splitlines()[1:] == diff_overwrite.splitlines()[1:] + assert r.changed + + r = task.run(files.write, + dry_run=False, + filename=filename, + content=content_b) + + assert not r.diff + assert not r.changed + + +def _test_append(task): + filename = "{}/{}-{}".format(BASEPATH, task.host, str(uuid.uuid4())) + + r = task.run(files.write, + dry_run=False, + filename=filename, + content=content_a) + + assert r.diff.splitlines()[1:] == diff_new.splitlines()[1:] + assert r.changed + + r = task.run(files.write, + dry_run=False, + filename=filename, + content=content_b, + append=True) + + assert r.diff.splitlines()[1:] == diff_append.splitlines()[1:] + assert r.changed + + +class Test(object): + + def test_write(self, brigade): + brigade.run(_test_write) + + def test_overwrite(self, brigade): + brigade.run(_test_overwrite) + + def test_append(self, brigade): + brigade.run(_test_append) From 29f4c561aa51844355f38e5efbadf709deafbe5e Mon Sep 17 00:00:00 2001 From: David Barroso Date: Sun, 24 Dec 2017 16:39:44 +0100 Subject: [PATCH 36/67] redundant --- .gitignore | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.gitignore b/.gitignore index 1bde5d19..8e0b0df3 100644 --- a/.gitignore +++ b/.gitignore @@ -96,8 +96,4 @@ tags .vars output/ -demo/log - -tests.log - .DS_Store From 2a926dfb7c168c1f3d481eceedf9e9c8899a0fbd Mon Sep 17 00:00:00 2001 From: David Barroso Date: Sun, 24 Dec 2017 16:39:58 +0100 Subject: [PATCH 37/67] log by default to ./brigade.log --- brigade/core/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/brigade/core/__init__.py b/brigade/core/__init__.py index 2a2dfbfc..92e82c4c 100644 --- a/brigade/core/__init__.py +++ b/brigade/core/__init__.py @@ -72,6 +72,7 @@ def __init__(self, inventory, dry_run, config=None, config_file=None, logging.basicConfig( level=logging.ERROR, format=format, + filename="brigade.log", ) if available_connections is not None: self.available_connections = available_connections From b0ca3e04deceabe523afe2445c718654cb42c9f3 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Sun, 24 Dec 2017 16:40:20 +0100 Subject: [PATCH 38/67] raise an error properly if we fail to connect to the device --- brigade/core/inventory.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/brigade/core/inventory.py b/brigade/core/inventory.py index 52c5caf9..a0481615 100644 --- a/brigade/core/inventory.py +++ b/brigade/core/inventory.py @@ -217,7 +217,9 @@ def get_connection(self, connection): # the given host. We also have to set `num_workers=1` because chances are # we are already inside a thread # Task should establish a connection and populate self.connection[connection] - self.brigade.filter(name=self.name).run(conn_task, num_workers=1) + r = self.brigade.filter(name=self.name).run(conn_task, num_workers=1) + if self.name in r.failed_hosts: + raise r.failed_hosts[self.name] return self.connections[connection] From 80631867cc99a88243c435b779850eb494cf7826 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Sun, 24 Dec 2017 16:40:56 +0100 Subject: [PATCH 39/67] added more devices to the lab --- examples/Vagrantfile | 39 ++++++++--- examples/groups.yaml | 12 ---- examples/hosts.yaml | 110 +++++++++++++++++++++++-------- examples/network_diagram.graffle | Bin 0 -> 2241 bytes examples/network_diagram.png | Bin 0 -> 114423 bytes 5 files changed, 110 insertions(+), 51 deletions(-) create mode 100644 examples/network_diagram.graffle create mode 100644 examples/network_diagram.png diff --git a/examples/Vagrantfile b/examples/Vagrantfile index bb2bcabb..d576a0d7 100644 --- a/examples/Vagrantfile +++ b/examples/Vagrantfile @@ -11,22 +11,41 @@ You will need the boxes: Vagrant.configure(2) do |config| config.vbguest.auto_update = false - config.vm.define "eos" do |eos| - eos.vm.box = "vEOS-lab-4.17.5M" + config.vm.define "spine00" do |spine00| + spine00.vm.box = "vEOS-lab-4.17.5M" - eos.vm.network :forwarded_port, guest: 443, host: 12443, id: 'https' + spine00.vm.network :forwarded_port, guest: 443, host: 12444, id: 'https' - eos.vm.network "private_network", virtualbox__intnet: "link_1", ip: "169.254.1.11", auto_config: false - eos.vm.network "private_network", virtualbox__intnet: "link_2", ip: "169.254.1.11", auto_config: false + spine00.vm.network "private_network", virtualbox__intnet: "link_1", ip: "169.254.1.11", auto_config: false + spine00.vm.network "private_network", virtualbox__intnet: "link_2", ip: "169.254.1.11", auto_config: false end - config.vm.define "junos" do |junos| - junos.vm.box = "juniper/ffp-12.1X47-D20.7-packetmode" + config.vm.define "spine01" do |spine01| + spine01.vm.box = "juniper/ffp-12.1X47-D20.7-packetmode" - junos.vm.network :forwarded_port, guest: 22, host: 12203, id: 'ssh' + spine01.vm.network :forwarded_port, guest: 22, host: 12204, id: 'ssh' - junos.vm.network "private_network", virtualbox__intnet: "link_1", ip: "169.254.1.11", auto_config: false - junos.vm.network "private_network", virtualbox__intnet: "link_2", ip: "169.254.1.11", auto_config: false + spine01.vm.network "private_network", virtualbox__intnet: "link_3", ip: "169.254.1.11", auto_config: false + spine01.vm.network "private_network", virtualbox__intnet: "link_4", ip: "169.254.1.11", auto_config: false + end + + + config.vm.define "leaf00" do |leaf00| + leaf00.vm.box = "vEOS-lab-4.17.5M" + + leaf00.vm.network :forwarded_port, guest: 443, host: 12443, id: 'https' + + leaf00.vm.network "private_network", virtualbox__intnet: "link_1", ip: "169.254.1.11", auto_config: false + leaf00.vm.network "private_network", virtualbox__intnet: "link_3", ip: "169.254.1.11", auto_config: false + end + + config.vm.define "leaf01" do |leaf01| + leaf01.vm.box = "juniper/ffp-12.1X47-D20.7-packetmode" + + leaf01.vm.network :forwarded_port, guest: 22, host: 12203, id: 'ssh' + + leaf01.vm.network "private_network", virtualbox__intnet: "link_2", ip: "169.254.1.11", auto_config: false + leaf01.vm.network "private_network", virtualbox__intnet: "link_4", ip: "169.254.1.11", auto_config: false end end diff --git a/examples/groups.yaml b/examples/groups.yaml index 54ce9cd4..0bfa38c7 100644 --- a/examples/groups.yaml +++ b/examples/groups.yaml @@ -3,20 +3,8 @@ all: group: null domain: acme.com -bma-leaf: - group: bma - -bma-host: - group: bma - bma: group: all -cmh-leaf: - group: cmh - -cmh-host: - group: cmh - cmh: group: all diff --git a/examples/hosts.yaml b/examples/hosts.yaml index 0a1b80db..c78cdb80 100644 --- a/examples/hosts.yaml +++ b/examples/hosts.yaml @@ -2,63 +2,115 @@ host1.cmh: site: cmh role: host - group: cmh-host - nos: linux + group: cmh + brigade_nos: linux + type: host host2.cmh: site: cmh role: host - group: cmh-host - nos: linux + group: cmh + brigade_nos: linux + type: host -switch00.cmh: - brigade_ip: 127.0.0.1 +spine00.cmh: + brigade_host: 127.0.0.1 brigade_username: vagrant brigade_password: vagrant - napalm_port: 12443 + brigade_network_api_port: 12444 + site: cmh + role: spine + group: cmh + brigade_nos: eos + type: network_device + +spine01.cmh: + brigade_host: 127.0.0.1 + brigade_username: vagrant + brigade_password: "" + brigade_network_api_port: 12204 + site: cmh + role: spine + group: cmh + brigade_nos: junos + type: network_device + +leaf00.cmh: + brigade_host: 127.0.0.1 + brigade_username: vagrant + brigade_password: vagrant + brigade_network_api_port: 12443 site: cmh role: leaf - group: cmh-leaf - nos: eos + group: cmh + brigade_nos: eos + type: network_device -switch01.cmh: - brigade_ip: 127.0.0.1 +leaf01.cmh: + brigade_host: 127.0.0.1 brigade_username: vagrant brigade_password: "" - napalm_port: 12203 + brigade_network_api_port: 12203 site: cmh role: leaf - group: cmh-leaf - nos: junos + group: cmh + brigade_nos: junos + type: network_device host1.bma: site: bma role: host - group: bma-host - nos: linux + group: bma + brigade_nos: linux + type: host host2.bma: site: bma role: host - group: bma-host - nos: linux + group: bma + brigade_nos: linux + type: host -switch00.bma: - brigade_ip: 127.0.0.1 +spine00.bma: + brigade_host: 127.0.0.1 brigade_username: vagrant brigade_password: vagrant - napalm_port: 12443 + brigade_network_api_port: 12444 site: bma - role: leaf - group: bma-leaf - nos: eos + role: spine + group: bma + brigade_nos: eos + type: network_device -switch01.bma: - brigade_ip: 127.0.0.1 +spine01.bma: + brigade_host: 127.0.0.1 brigade_username: vagrant brigade_password: "" - napalm_port: 12203 + brigade_network_api_port: 12204 + site: bma + role: spine + group: bma + brigade_nos: junos + type: network_device + +leaf00.bma: + brigade_host: 127.0.0.1 + brigade_username: vagrant + brigade_password: vagrant + brigade_network_api_port: 12443 + site: bma + role: leaf + group: bma + brigade_nos: eos + type: network_device + +leaf01.bma: + brigade_host: 127.0.0.1 + brigade_username: vagrant + brigade_password: wrong_password + brigade_network_api_port: 12203 site: bma role: leaf - group: bma-leaf - nos: junos + group: bma + brigade_nos: junos + type: network_device diff --git a/examples/network_diagram.graffle b/examples/network_diagram.graffle new file mode 100644 index 0000000000000000000000000000000000000000..2711ab7a46b091920b996549a197737a84559376 GIT binary patch literal 2241 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 Date: Wed, 27 Dec 2017 16:10:23 +0100 Subject: [PATCH 40/67] examples WIP --- examples/1_simple_runbooks/backup.py | 28 ++++++ examples/1_simple_runbooks/configure.py | 53 +++++++++++ examples/1_simple_runbooks/get_facts.py | 26 ++++++ examples/1_simple_runbooks/validate.py | 54 ++++++++++++ examples/2_simple_tooling/backup.py | 56 ++++++++++++ examples/2_simple_tooling/configure.py | 69 +++++++++++++++ examples/2_simple_tooling/get_facts.py | 50 +++++++++++ examples/2_simple_tooling/validate.py | 70 +++++++++++++++ examples/3_advanced_tooling/mate.py | 43 +++++++++ examples/3_advanced_tooling/tasks/__init__.py | 0 examples/3_advanced_tooling/tasks/backup.py | 37 ++++++++ .../3_advanced_tooling/tasks/configure.py | 77 ++++++++++++++++ .../3_advanced_tooling/tasks/get_facts.py | 21 +++++ examples/3_advanced_tooling/tasks/validate.py | 49 +++++++++++ examples/configure.py | 82 ------------------ examples/extra_data/leaf00.cmh/l3.yaml | 16 ++++ examples/extra_data/leaf01.cmh/l3.yaml | 16 ++++ examples/extra_data/spine00.cmh/l3.yaml | 16 ++++ examples/extra_data/spine01.cmh/l3.yaml | 16 ++++ .../extra_data/switch00.bma/interfaces.yaml | 7 -- .../extra_data/switch00.cmh/interfaces.yaml | 7 -- .../extra_data/switch01.bma/interfaces.yaml | 7 -- .../extra_data/switch01.cmh/interfaces.yaml | 7 -- examples/get_facts_grouping.py | 42 --------- examples/get_facts_simple.py | 34 -------- examples/groups.yaml | 4 + examples/hosts.yaml | 2 + examples/network_diagram.graffle | Bin 2241 -> 2671 bytes examples/templates/{base => }/eos/base.j2 | 0 examples/templates/eos/interfaces.j2 | 8 ++ examples/templates/eos/leaf.j2 | 4 + examples/templates/eos/routing.j2 | 12 +++ examples/templates/eos/spine.j2 | 0 .../templates/interfaces/eos/interfaces.j2 | 6 -- .../templates/interfaces/junos/interfaces.j2 | 9 -- examples/templates/{base => }/junos/base.j2 | 0 examples/templates/junos/interfaces.j2 | 15 ++++ examples/templates/junos/leaf.j2 | 8 ++ examples/templates/junos/routing.j2 | 30 +++++++ examples/templates/junos/spine.j2 | 0 .../plugins/tasks/data/test_data/simple.json | 2 +- 41 files changed, 781 insertions(+), 202 deletions(-) create mode 100755 examples/1_simple_runbooks/backup.py create mode 100755 examples/1_simple_runbooks/configure.py create mode 100755 examples/1_simple_runbooks/get_facts.py create mode 100755 examples/1_simple_runbooks/validate.py create mode 100755 examples/2_simple_tooling/backup.py create mode 100755 examples/2_simple_tooling/configure.py create mode 100755 examples/2_simple_tooling/get_facts.py create mode 100755 examples/2_simple_tooling/validate.py create mode 100755 examples/3_advanced_tooling/mate.py create mode 100644 examples/3_advanced_tooling/tasks/__init__.py create mode 100755 examples/3_advanced_tooling/tasks/backup.py create mode 100755 examples/3_advanced_tooling/tasks/configure.py create mode 100755 examples/3_advanced_tooling/tasks/get_facts.py create mode 100755 examples/3_advanced_tooling/tasks/validate.py delete mode 100644 examples/configure.py create mode 100644 examples/extra_data/leaf00.cmh/l3.yaml create mode 100644 examples/extra_data/leaf01.cmh/l3.yaml create mode 100644 examples/extra_data/spine00.cmh/l3.yaml create mode 100644 examples/extra_data/spine01.cmh/l3.yaml delete mode 100644 examples/extra_data/switch00.bma/interfaces.yaml delete mode 100644 examples/extra_data/switch00.cmh/interfaces.yaml delete mode 100644 examples/extra_data/switch01.bma/interfaces.yaml delete mode 100644 examples/extra_data/switch01.cmh/interfaces.yaml delete mode 100644 examples/get_facts_grouping.py delete mode 100644 examples/get_facts_simple.py rename examples/templates/{base => }/eos/base.j2 (100%) create mode 100644 examples/templates/eos/interfaces.j2 create mode 100644 examples/templates/eos/leaf.j2 create mode 100644 examples/templates/eos/routing.j2 create mode 100644 examples/templates/eos/spine.j2 delete mode 100644 examples/templates/interfaces/eos/interfaces.j2 delete mode 100644 examples/templates/interfaces/junos/interfaces.j2 rename examples/templates/{base => }/junos/base.j2 (100%) create mode 100644 examples/templates/junos/interfaces.j2 create mode 100644 examples/templates/junos/leaf.j2 create mode 100644 examples/templates/junos/routing.j2 create mode 100644 examples/templates/junos/spine.j2 diff --git a/examples/1_simple_runbooks/backup.py b/examples/1_simple_runbooks/backup.py new file mode 100755 index 00000000..84f247f1 --- /dev/null +++ b/examples/1_simple_runbooks/backup.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +from brigade.easy import easy_brigade +from brigade.plugins.tasks import files, networking, text + + +def backup(task): + result = task.run(networking.napalm_get, + getters="config") + + return task.run(files.write, + content=result.result["config"]["running"], + filename="./backups/{}".format(task.host)) + + +brigade = easy_brigade( + hosts="../hosts.yaml", groups="../groups.yaml", + dry_run=False, + raise_on_error=False, +) + +# select which devices we want to work with +filtered = brigade.filter(type="network_device", site="cmh") +results = filtered.run(backup) + +# Let's print the result on screen +filtered.run(text.print_result, + num_workers=1, # we are printing on screen so we want to do this synchronously + data=results) diff --git a/examples/1_simple_runbooks/configure.py b/examples/1_simple_runbooks/configure.py new file mode 100755 index 00000000..9f44d08a --- /dev/null +++ b/examples/1_simple_runbooks/configure.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +""" +In this example we write a CLI tool with brigade and click to deploy configuration. +""" +from brigade.core import Brigade +from brigade.core.configuration import Config +from brigade.plugins.inventory.simple import SimpleInventory +from brigade.plugins.tasks import data, networking, text + + +def configure(task): + r = task.run(text.template_file, + template="base.j2", + path="../templates/{brigade_nos}") + task.host["config"] = r.result + + r = task.run(data.load_yaml, + file="../extra_data/{host}/l3.yaml") + task.host["l3"] = r.result + + r = task.run(text.template_file, + template="interfaces.j2", + path="../templates/{brigade_nos}") + task.host["config"] += r.result + + r = task.run(text.template_file, + template="routing.j2", + path="../templates/{brigade_nos}") + task.host["config"] += r.result + + r = task.run(text.template_file, + template="{role}.j2", + path="../templates/{brigade_nos}") + task.host["config"] += r.result + + return task.run(networking.napalm_configure, + replace=False, + configuration=task.host["config"]) + + +brigade = Brigade( + inventory=SimpleInventory("../hosts.yaml", "../groups.yaml"), + dry_run=False, + config=Config(raise_on_error=False), +) + +filtered = brigade.filter(type="network_device", site="cmh") + +results = filtered.run(task=configure) + +filtered.run(text.print_result, + num_workers=1, # we are printing on screen so we want to do this synchronously + data=results) diff --git a/examples/1_simple_runbooks/get_facts.py b/examples/1_simple_runbooks/get_facts.py new file mode 100755 index 00000000..5defeda5 --- /dev/null +++ b/examples/1_simple_runbooks/get_facts.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +""" +This is a very simple scripts to get facts and print them on the screen. +""" +from brigade.core import Brigade +from brigade.core.configuration import Config +from brigade.plugins.inventory.simple import SimpleInventory +from brigade.plugins.tasks import networking, text + +brigade = Brigade( + inventory=SimpleInventory("../hosts.yaml", "../groups.yaml"), + dry_run=True, + config=Config(raise_on_error=False), +) + +# select which devices we want to work with +filtered = brigade.filter(type="network_device", site="cmh") + +# we are going to gather "interfaces" and "facts" information with napalm +results = filtered.run(networking.napalm_get, + getters=["interfaces", "facts"]) + +# Let's print the result on screen +filtered.run(text.print_result, + num_workers=1, # we are printing on screen so we want to do this synchronously + data=results) diff --git a/examples/1_simple_runbooks/validate.py b/examples/1_simple_runbooks/validate.py new file mode 100755 index 00000000..6ef85baf --- /dev/null +++ b/examples/1_simple_runbooks/validate.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +""" +In this example we write a CLI tool with brigade and click to deploy configuration. +""" +from brigade.core import Brigade +from brigade.plugins.inventory.simple import SimpleInventory +from brigade.plugins.tasks import data, networking, text + + +def validate(task): + task.host["config"] = "" + + r = task.run(name="read data", + task=data.load_yaml, + file="../extra_data/{host}/l3.yaml") + + validation_rules = [{ + 'get_bgp_neighbors': { + 'global': { + 'peers': { + '_mode': 'strict', + } + } + } + }] + peers = validation_rules[0]['get_bgp_neighbors']['global']['peers'] + for session in r.result['sessions']: + peers[session['ipv4']] = {'is_up': True} + + return task.run(name="validating data", + task=networking.napalm_validate, + validation_source=validation_rules) + + +def print_compliance(task, results): + task.run(name="print result", + task=text.print_result, + data=results[task.host.name], + failed=not results[task.host.name].result['complies'], + ) + + +brigade = Brigade( + inventory=SimpleInventory("../hosts.yaml", "../groups.yaml"), + dry_run=False, +) + +filtered = brigade.filter(type="network_device", site="cmh") + +results = filtered.run(task=validate) + +filtered.run(print_compliance, + results=results, + num_workers=1) diff --git a/examples/2_simple_tooling/backup.py b/examples/2_simple_tooling/backup.py new file mode 100755 index 00000000..bf68c168 --- /dev/null +++ b/examples/2_simple_tooling/backup.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +""" +This is a simple example where we use click and brigade to build a simple CLI tool to retrieve +hosts information. + +The main difference with get_facts_simple.py is that instead of calling a plugin directly +we wrap it in a function. It is not very useful or necessary here but illustrates how +tasks can be grouped. +""" +from brigade.core import Brigade +from brigade.core.configuration import Config +from brigade.plugins.inventory.simple import SimpleInventory +from brigade.plugins.tasks import files, networking, text + +import click + + +def backup(task, path): + result = task.run(networking.napalm_get, + getters="config") + + return task.run(files.write, + content=result.result["config"]["running"], + filename="{}/{}".format(path, task.host)) + + +@click.command() +@click.option('--filter', '-f', multiple=True) +@click.option('--path', '-p', default=".") +def main(filter, path): + """ + Backups running configuration of devices into a file + """ + brigade = Brigade( + inventory=SimpleInventory("../hosts.yaml", "../groups.yaml"), + dry_run=False, + config=Config(raise_on_error=False), + ) + + # filter is going to be a list of key=value so we clean that first + filter_dict = {"type": "network_device"} + for f in filter: + k, v = f.split("=") + filter_dict[k] = v + + filtered = brigade.filter(**filter_dict) # let's filter the devices + results = filtered.run(backup, num_workers=20, path=path) + + # Let's print the result on screen + filtered.run(text.print_result, + num_workers=1, # we are printing on screen so we want to do this synchronously + data=results) + + +if __name__ == "__main__": + main() diff --git a/examples/2_simple_tooling/configure.py b/examples/2_simple_tooling/configure.py new file mode 100755 index 00000000..f0a16632 --- /dev/null +++ b/examples/2_simple_tooling/configure.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +""" +In this example we write a CLI tool with brigade and click to deploy configuration. +""" +from brigade.core import Brigade +from brigade.core.configuration import Config +from brigade.plugins.inventory.simple import SimpleInventory +from brigade.plugins.tasks import data, networking, text + +import click + + +def configure(task): + r = task.run(text.template_file, + template="base.j2", + path="../templates/{brigade_nos}") + task.host["config"] = r.result + + r = task.run(data.load_yaml, + file="../extra_data/{host}/l3.yaml") + task.host["l3"] = r.result + + r = task.run(text.template_file, + template="interfaces.j2", + path="../templates/{brigade_nos}") + task.host["config"] += r.result + + r = task.run(text.template_file, + template="routing.j2", + path="../templates/{brigade_nos}") + task.host["config"] += r.result + + r = task.run(text.template_file, + template="{role}.j2", + path="../templates/{brigade_nos}") + task.host["config"] += r.result + + return task.run(networking.napalm_configure, + replace=False, + configuration=task.host["config"]) + + +@click.command() +@click.option('--filter', '-f', multiple=True) +@click.option('--commit/--no-commit', '-c', default=False) +def main(filter, commit): + brigade = Brigade( + inventory=SimpleInventory("../hosts.yaml", "../groups.yaml"), + dry_run=not commit, + config=Config(raise_on_error=False), + ) + + # filter is going to be a list of key=value so we clean that first + filter_dict = {"type": "network_device"} + for f in filter: + k, v = f.split("=") + filter_dict[k] = v + + filtered = brigade.filter(**filter_dict) # let's filter the devices + + results = filtered.run(task=configure) + + filtered.run(text.print_result, + num_workers=1, # we are printing on screen so we want to do this synchronously + data=results) + + +if __name__ == "__main__": + main() diff --git a/examples/2_simple_tooling/get_facts.py b/examples/2_simple_tooling/get_facts.py new file mode 100755 index 00000000..b8629905 --- /dev/null +++ b/examples/2_simple_tooling/get_facts.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +""" +This is a simple example where we use click and brigade to build a simple CLI tool to retrieve +hosts information. + +The main difference with get_facts_simple.py is that instead of calling a plugin directly +we wrap it in a function. It is not very useful or necessary here but illustrates how +tasks can be grouped. +""" +from brigade.core import Brigade +from brigade.core.configuration import Config +from brigade.plugins.inventory.simple import SimpleInventory +from brigade.plugins.tasks import networking, text + +import click + + +@click.command() +@click.option('--filter', '-f', multiple=True, + help="k=v pairs to filter the devices") +@click.option('--get', '-g', multiple=True, + help="getters you want to use") +def main(filter, get): + """ + Retrieve information from network devices using napalm + """ + brigade = Brigade( + inventory=SimpleInventory("../hosts.yaml", "../groups.yaml"), + dry_run=True, + config=Config(raise_on_error=False), + ) + + # filter is going to be a list of key=value so we clean that first + filter_dict = {"type": "network_device"} + for f in filter: + k, v = f.split("=") + filter_dict[k] = v + + filtered = brigade.filter(**filter_dict) # let's filter the devices + results = filtered.run(networking.napalm_get, + getters=get) + + # Let's print the result on screen + filtered.run(text.print_result, + num_workers=1, # we are printing on screen so we want to do this synchronously + data=results) + + +if __name__ == "__main__": + main() diff --git a/examples/2_simple_tooling/validate.py b/examples/2_simple_tooling/validate.py new file mode 100755 index 00000000..a64a928f --- /dev/null +++ b/examples/2_simple_tooling/validate.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +""" +In this example we write a CLI tool with brigade and click to deploy configuration. +""" +from brigade.core import Brigade +from brigade.plugins.inventory.simple import SimpleInventory +from brigade.plugins.tasks import data, networking, text + +import click + + +def validate(task): + task.host["config"] = "" + + r = task.run(name="read data", + task=data.load_yaml, + file="../extra_data/{host}/l3.yaml") + + validation_rules = [{ + 'get_bgp_neighbors': { + 'global': { + 'peers': { + '_mode': 'strict', + } + } + } + }] + peers = validation_rules[0]['get_bgp_neighbors']['global']['peers'] + for session in r.result['sessions']: + peers[session['ipv4']] = {'is_up': True} + + return task.run(name="validating data", + task=networking.napalm_validate, + validation_source=validation_rules) + + +def print_compliance(task, results): + task.run(name="print result", + task=text.print_result, + data=results[task.host.name], + failed=not results[task.host.name].result['complies'], + ) + + +@click.command() +@click.option('--filter', '-f', multiple=True) +@click.option('--commit/--no-commit', '-c', default=False) +def main(filter, commit): + brigade = Brigade( + inventory=SimpleInventory("../hosts.yaml", "../groups.yaml"), + dry_run=False, + ) + + # filter is going to be a list of key=value so we clean that first + filter_dict = {"type": "network_device"} + for f in filter: + k, v = f.split("=") + filter_dict[k] = v + + filtered = brigade.filter(**filter_dict) # let's filter the devices + + results = filtered.run(task=validate) + + filtered.run(print_compliance, + results=results, + num_workers=1) + + +if __name__ == "__main__": + main() diff --git a/examples/3_advanced_tooling/mate.py b/examples/3_advanced_tooling/mate.py new file mode 100755 index 00000000..e78acd91 --- /dev/null +++ b/examples/3_advanced_tooling/mate.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +""" +In this example we write a CLI tool with brigade and click to deploy configuration. +""" + +from brigade.core import Brigade +from brigade.core.configuration import Config +from brigade.plugins.inventory.simple import SimpleInventory + +import click + +from tasks import backup, configure, get_facts, validate + + +@click.group() +@click.option('--filter', '-f', multiple=True) +@click.option('--commit/--no-commit', '-c', default=False) +@click.pass_context +def run(ctx, filter, commit): + brigade = Brigade( + inventory=SimpleInventory("../hosts.yaml", "../groups.yaml"), + dry_run=not commit, + config=Config(raise_on_error=False), + ) + + # filter is going to be a list of key=value so we clean that first + filter_dict = {"type": "network_device"} + for f in filter: + k, v = f.split("=") + filter_dict[k] = v + + filtered = brigade.filter(**filter_dict) # let's filter the devices + ctx.obj["filtered"] = filtered + + +run.add_command(backup.backup) +run.add_command(configure.configure) +run.add_command(get_facts.get) +run.add_command(validate.validate) + + +if __name__ == "__main__": + run(obj={}) diff --git a/examples/3_advanced_tooling/tasks/__init__.py b/examples/3_advanced_tooling/tasks/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/3_advanced_tooling/tasks/backup.py b/examples/3_advanced_tooling/tasks/backup.py new file mode 100755 index 00000000..67262c38 --- /dev/null +++ b/examples/3_advanced_tooling/tasks/backup.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +""" +This is a simple example where we use click and brigade to build a simple CLI tool to retrieve +hosts information. + +The main difference with get_facts_simple.py is that instead of calling a plugin directly +we wrap it in a function. It is not very useful or necessary here but illustrates how +tasks can be grouped. +""" +from brigade.plugins import functions +from brigade.plugins.tasks import files, networking, text + +import click + + +def backup_task(task, path): + result = task.run(networking.napalm_get, + getters="config") + + return task.run(files.write, + content=result.result["config"]["running"], + filename="{}/{}".format(path, task.host)) + + +@click.command() +@click.option('--backup-path', default=".") +@click.pass_context +def backup(ctx, backup_path, **kwargs): + functions.text.print_title("Backing up configurations") + filtered = ctx.obj["filtered"] + results = filtered.run(backup_task, + path=backup_path) + + # Let's print the result on screen + return filtered.run(text.print_result, + num_workers=1, + data=results) diff --git a/examples/3_advanced_tooling/tasks/configure.py b/examples/3_advanced_tooling/tasks/configure.py new file mode 100755 index 00000000..6ef6c512 --- /dev/null +++ b/examples/3_advanced_tooling/tasks/configure.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +""" +In this example we write a CLI tool with brigade and click to deploy configuration. +""" +import time + +from brigade.plugins import functions +from brigade.plugins.tasks import data, networking, text + +import click + +from . import backup as backup_ +from . import validate as validate_ + + +def configure_task(task): + r = task.run(text.template_file, + template="base.j2", + path="../templates/{brigade_nos}") + task.host["config"] = r.result + + r = task.run(data.load_yaml, + file="../extra_data/{host}/l3.yaml") + task.host["l3"] = r.result + + r = task.run(text.template_file, + template="interfaces.j2", + path="../templates/{brigade_nos}") + task.host["config"] += r.result + + r = task.run(text.template_file, + template="routing.j2", + path="../templates/{brigade_nos}") + task.host["config"] += r.result + + r = task.run(text.template_file, + template="{role}.j2", + path="../templates/{brigade_nos}") + task.host["config"] += r.result + + return task.run(networking.napalm_configure, + replace=False, + configuration=task.host["config"]) + + +@click.command() +@click.option("--validate/--no-validate", default=False) +@click.option("--rollback/--no-rollback", default=False) +@click.option("--backup/--no-backup", default=False) +@click.option('--backup-path', default=".") +@click.pass_context +def configure(ctx, validate, backup, backup_path, rollback): + filtered = ctx.obj["filtered"] + + if backup: + backup_.backup.invoke(ctx) + + functions.text.print_title("Configure Network") + results = filtered.run(task=configure_task) + + filtered.run(text.print_result, + num_workers=1, # we are printing on screen so we want to do this synchronously + data=results) + + if validate: + time.sleep(10) + r = validate_.validate.invoke(ctx) + + if r.failed and rollback: + functions.text.print_title("Rolling back configuration!!!") + r = filtered.run(networking.napalm_configure, + replace=True, + filename=backup_path + "/{host}") + filtered.run(text.print_result, + num_workers=1, + data=r) + import pdb; pdb.set_trace() # noqa diff --git a/examples/3_advanced_tooling/tasks/get_facts.py b/examples/3_advanced_tooling/tasks/get_facts.py new file mode 100755 index 00000000..e31dcdd9 --- /dev/null +++ b/examples/3_advanced_tooling/tasks/get_facts.py @@ -0,0 +1,21 @@ +from brigade.plugins.tasks import networking, text + +import click + + +@click.command() +@click.option('--get', '-g', multiple=True, + help="getters you want to use") +@click.pass_context +def get(ctx, get): + """ + Retrieve information from network devices using napalm + """ + filtered = ctx.obj["filtered"] + results = filtered.run(networking.napalm_get, + getters=get) + + # Let's print the result on screen + filtered.run(text.print_result, + num_workers=1, # we are printing on screen so we want to do this synchronously + data=results) diff --git a/examples/3_advanced_tooling/tasks/validate.py b/examples/3_advanced_tooling/tasks/validate.py new file mode 100755 index 00000000..a22bf30b --- /dev/null +++ b/examples/3_advanced_tooling/tasks/validate.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +""" +In this example we write a CLI tool with brigade and click to deploy configuration. +""" +from brigade.plugins import functions +from brigade.plugins.tasks import data, networking, text + +import click + + +def validate_task(task): + task.host["config"] = "" + + r = task.run(name="read data", + task=data.load_yaml, + file="../extra_data/{host}/l3.yaml") + + validation_rules = [{ + 'get_bgp_neighbors': { + 'global': { + 'peers': { + '_mode': 'strict', + } + } + } + }] + peers = validation_rules[0]['get_bgp_neighbors']['global']['peers'] + for session in r.result['sessions']: + peers[session['ipv4']] = {'is_up': True} + + return task.run(name="validating data", + task=networking.napalm_validate, + validation_source=validation_rules) + + +@click.command() +@click.pass_context +def validate(ctx, **kwargs): + functions.text.print_title("Make sure BGP sessions are UP") + filtered = ctx.obj["filtered"] + + results = filtered.run(task=validate_task) + + filtered.run(name="print validate result", + num_workers=1, + task=text.print_result, + data=results) + + return results diff --git a/examples/configure.py b/examples/configure.py deleted file mode 100644 index f88334cf..00000000 --- a/examples/configure.py +++ /dev/null @@ -1,82 +0,0 @@ -""" -In this example we write a CLI tool with brigade and click to deploy configuration. -""" -import logging - -from brigade.core import Brigade -from brigade.plugins.inventory.simple import SimpleInventory -from brigade.plugins.tasks import data, networking, text - -import click - - -def base_config(task): - """ - 1. logs all the facts, even the ones inherited from groups - 2. Creates a placeholder for device configuration - 3. Initializes some basic configuration - """ - logging.info({task.host.name: task.host.items()}) - - task.host["config"] = "" - - r = text.template_file(task=task, - template="base.j2", - path="templates/base/{nos}") - task.host["config"] += r.result - - -def configure_interfaces(task): - """ - 1. Load interface data from an external yaml file - 2. Creates interface configuration - """ - r = data.load_yaml(task=task, - file="extra_data/{host}/interfaces.yaml") - task.host["interfaces"] = r.result - - r = text.template_file(task=task, - template="interfaces.j2", - path="templates/interfaces/{nos}") - task.host["config"] += r.result - - -def deploy_config(task): - """ - 1. Load configuration into the device - 2. Prints diff - """ - r = networking.napalm_configure(task=task, - replace=False, - configuration=task.host["config"]) - - click.secho("--- {} ({})".format(task.host, r.changed), fg="blue", bold=True) - click.secho(r.diff, fg='yellow') - click.echo() - - -@click.command() -@click.option('--commit/--no-commit', default=False) -@click.option('--debug/--no-debug', default=False) -@click.argument('site') -@click.argument('role') -def deploy(commit, debug, site, role): - logging.basicConfig( - filename="log", - level=logging.DEBUG if debug else logging.INFO, - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', - ) - - brigade = Brigade( - inventory=SimpleInventory("hosts.yaml", "groups.yaml"), - dry_run=not commit, - ) - - filtered = brigade.filter(site=site, role=role) - filtered.run(task=base_config) - filtered.run(task=configure_interfaces) - filtered.run(task=deploy_config) - - -if __name__ == "__main__": - deploy() diff --git a/examples/extra_data/leaf00.cmh/l3.yaml b/examples/extra_data/leaf00.cmh/l3.yaml new file mode 100644 index 00000000..f5fb9854 --- /dev/null +++ b/examples/extra_data/leaf00.cmh/l3.yaml @@ -0,0 +1,16 @@ +--- +interfaces: + Ethernet1: + connects_to: spine00.cmh + ipv4: 10.0.0.1/31 + enabled: false + Ethernet2: + connects_to: spine01.cmh + ipv4: 10.0.1.1/31 + enabled: true + +sessions: + - ipv4: 10.0.0.0 + peer_as: 65000 + - ipv4: 10.0.1.0 + peer_as: 65000 diff --git a/examples/extra_data/leaf01.cmh/l3.yaml b/examples/extra_data/leaf01.cmh/l3.yaml new file mode 100644 index 00000000..f3fb1c5c --- /dev/null +++ b/examples/extra_data/leaf01.cmh/l3.yaml @@ -0,0 +1,16 @@ +--- +interfaces: + ge-0/0/1: + connects_to: spine00.cmh + ipv4: 10.0.0.3/31 + enabled: true + ge-0/0/2: + connects_to: spine01.cmh + ipv4: 10.0.1.3/31 + enabled: true + +sessions: + - ipv4: 10.0.0.2 + peer_as: 65000 + - ipv4: 10.0.1.2 + peer_as: 65000 diff --git a/examples/extra_data/spine00.cmh/l3.yaml b/examples/extra_data/spine00.cmh/l3.yaml new file mode 100644 index 00000000..19c58537 --- /dev/null +++ b/examples/extra_data/spine00.cmh/l3.yaml @@ -0,0 +1,16 @@ +--- +interfaces: + Ethernet1: + connects_to: leaf00.cmh + ipv4: 10.0.0.0/31 + enabled: true + Ethernet2: + connects_to: leaf01.cmh + ipv4: 10.0.0.2/31 + enabled: true + +sessions: + - ipv4: 10.0.0.1 + peer_as: 65100 + - ipv4: 10.0.0.3 + peer_as: 65101 diff --git a/examples/extra_data/spine01.cmh/l3.yaml b/examples/extra_data/spine01.cmh/l3.yaml new file mode 100644 index 00000000..7bdd33cc --- /dev/null +++ b/examples/extra_data/spine01.cmh/l3.yaml @@ -0,0 +1,16 @@ +--- +interfaces: + ge-0/0/1: + connects_to: leaf00.cmh + ipv4: 10.0.1.0/31 + enabled: true + ge-0/0/2: + connects_to: leaf01.cmh + ipv4: 10.0.1.2/31 + enabled: true + +sessions: + - ipv4: 10.0.1.1 + peer_as: 65100 + - ipv4: 10.0.1.3 + peer_as: 65101 diff --git a/examples/extra_data/switch00.bma/interfaces.yaml b/examples/extra_data/switch00.bma/interfaces.yaml deleted file mode 100644 index 64d0d9b6..00000000 --- a/examples/extra_data/switch00.bma/interfaces.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -Ethernet1: - description: "An Interface in bma" - enabled: true -Ethernet2: - description: "Another interface in bma" - enabled: false diff --git a/examples/extra_data/switch00.cmh/interfaces.yaml b/examples/extra_data/switch00.cmh/interfaces.yaml deleted file mode 100644 index 4f0c5e0b..00000000 --- a/examples/extra_data/switch00.cmh/interfaces.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -Ethernet1: - description: "An Interface in cmh" - enabled: true -Ethernet2: - description: "Another interface in cmh" - enabled: false diff --git a/examples/extra_data/switch01.bma/interfaces.yaml b/examples/extra_data/switch01.bma/interfaces.yaml deleted file mode 100644 index db67fca1..00000000 --- a/examples/extra_data/switch01.bma/interfaces.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -ge-0/0/1: - description: "An Interface in bma" - enabled: true -ge-0/0/2: - description: "Another interface in bma" - enabled: false diff --git a/examples/extra_data/switch01.cmh/interfaces.yaml b/examples/extra_data/switch01.cmh/interfaces.yaml deleted file mode 100644 index e847b411..00000000 --- a/examples/extra_data/switch01.cmh/interfaces.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -ge-0/0/1: - description: "An Interface in cmh" - enabled: true -ge-0/0/2: - description: "Another interface in cmh" - enabled: false diff --git a/examples/get_facts_grouping.py b/examples/get_facts_grouping.py deleted file mode 100644 index f016dd96..00000000 --- a/examples/get_facts_grouping.py +++ /dev/null @@ -1,42 +0,0 @@ -""" -This is a simple example where we use click and brigade to build a simple CLI tool to retrieve -hosts information. - -The main difference with get_facts_simple.py is that instead of calling a plugin directly -we wrap it in a function. It is not very useful or necessary here but illustrates how -tasks can be grouped. -""" -from brigade.core import Brigade -from brigade.plugins.inventory.simple import SimpleInventory -from brigade.plugins.tasks import networking - -import click - - -def get_facts(task, facts): - return networking.napalm_get_facts(task, facts) - - -@click.command() -@click.argument('site') -@click.argument('role') -@click.argument('facts') -def main(site, role, facts): - brigade = Brigade( - inventory=SimpleInventory("hosts.yaml", "groups.yaml"), - dry_run=True, - ) - - filtered = brigade.filter(site=site, role=role) - result = filtered.run(task=get_facts, - facts=facts) - - for host, r in result.items(): - print(host) - print("============") - print(r.result) - print() - - -if __name__ == "__main__": - main() diff --git a/examples/get_facts_simple.py b/examples/get_facts_simple.py deleted file mode 100644 index 5cbc1fbd..00000000 --- a/examples/get_facts_simple.py +++ /dev/null @@ -1,34 +0,0 @@ -""" -This is a simple example where we use click and brigade to build a simple CLI tool to retrieve -hosts information. -""" -from brigade.core import Brigade -from brigade.plugins.inventory.simple import SimpleInventory -from brigade.plugins.tasks import networking - -import click - - -@click.command() -@click.argument('site') -@click.argument('role') -@click.argument('facts') -def main(site, role, facts): - brigade = Brigade( - inventory=SimpleInventory("hosts.yaml", "groups.yaml"), - dry_run=True, - ) - - filtered = brigade.filter(site=site, role=role) - result = filtered.run(task=networking.napalm_get_facts, - facts=facts) - - for host, r in result.items(): - print(host) - print("============") - print(r.result) - print() - - -if __name__ == "__main__": - main() diff --git a/examples/groups.yaml b/examples/groups.yaml index 0bfa38c7..df5b6ba5 100644 --- a/examples/groups.yaml +++ b/examples/groups.yaml @@ -8,3 +8,7 @@ bma: cmh: group: all + asn: 65000 + vlans: + 100: frontend + 200: backend diff --git a/examples/hosts.yaml b/examples/hosts.yaml index c78cdb80..15ccd489 100644 --- a/examples/hosts.yaml +++ b/examples/hosts.yaml @@ -45,6 +45,7 @@ leaf00.cmh: group: cmh brigade_nos: eos type: network_device + asn: 65100 leaf01.cmh: brigade_host: 127.0.0.1 @@ -56,6 +57,7 @@ leaf01.cmh: group: cmh brigade_nos: junos type: network_device + asn: 65101 host1.bma: site: bma diff --git a/examples/network_diagram.graffle b/examples/network_diagram.graffle index 2711ab7a46b091920b996549a197737a84559376..871fb479332e1970f173a8bfdc1b1c8a05586a2e 100644 GIT binary patch literal 2671 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 Date: Wed, 27 Dec 2017 16:26:02 +0100 Subject: [PATCH 41/67] improvements to napalm_configure --- .../plugins/tasks/networking/napalm_configure.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/brigade/plugins/tasks/networking/napalm_configure.py b/brigade/plugins/tasks/networking/napalm_configure.py index 895d06b4..663c8404 100644 --- a/brigade/plugins/tasks/networking/napalm_configure.py +++ b/brigade/plugins/tasks/networking/napalm_configure.py @@ -1,7 +1,8 @@ +from brigade.core.helpers import format_string from brigade.core.task import Result -def napalm_configure(task, configuration, replace=False): +def napalm_configure(task, filename=None, configuration=None, replace=False): """ Loads configuration into a network devices using napalm @@ -15,15 +16,16 @@ def napalm_configure(task, configuration, replace=False): * diff (``string``): change in the system """ device = task.host.get_connection("napalm") + filename = format_string(filename, task, **task.host) if filename is not None else None if replace: - device.load_replace_candidate(config=configuration) + device.load_replace_candidate(filename=filename, config=configuration) else: - device.load_merge_candidate(config=configuration) + device.load_merge_candidate(filename=filename, config=configuration) diff = device.compare_config() - if task.dry_run: - device.discard_config() - else: + if not task.dry_run and diff: device.commit_config() + else: + device.discard_config() return Result(host=task.host, diff=diff, changed=len(diff) > 0) From 31846323a48c1b184e7a04792fbe368665f91399 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Wed, 27 Dec 2017 16:27:17 +0100 Subject: [PATCH 42/67] unnecessary --- brigade/core/inventory.py | 4 ---- brigade/core/task.py | 3 --- brigade/plugins/tasks/commands/command.py | 5 ----- brigade/plugins/tasks/commands/remote_command.py | 5 ----- brigade/plugins/tasks/files/sftp.py | 4 ---- 5 files changed, 21 deletions(-) diff --git a/brigade/core/inventory.py b/brigade/core/inventory.py index a0481615..adb5186e 100644 --- a/brigade/core/inventory.py +++ b/brigade/core/inventory.py @@ -1,12 +1,8 @@ import getpass -import logging from brigade.core import helpers -logger = logging.getLogger("brigade") - - class Host(object): """ Represents a host. diff --git a/brigade/core/task.py b/brigade/core/task.py index 72a1e694..34006e85 100644 --- a/brigade/core/task.py +++ b/brigade/core/task.py @@ -1,10 +1,7 @@ -import logging from builtins import super from brigade.core.exceptions import BrigadeExecutionError -logger = logging.getLogger("brigade") - class Task(object): """ diff --git a/brigade/plugins/tasks/commands/command.py b/brigade/plugins/tasks/commands/command.py index 3efc587a..718bed4e 100644 --- a/brigade/plugins/tasks/commands/command.py +++ b/brigade/plugins/tasks/commands/command.py @@ -1,4 +1,3 @@ -import logging import shlex import subprocess @@ -8,9 +7,6 @@ from brigade.core.task import Result -logger = logging.getLogger("brigade") - - def command(task, command): """ Executes a command locally @@ -27,7 +23,6 @@ def command(task, command): :obj:`brigade.core.exceptions.CommandError`: when there is a command error """ command = format_string(command, task, **task.host) - logger.debug("{}:local_cmd:{}".format(task.host, command)) cmd = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE, diff --git a/brigade/plugins/tasks/commands/remote_command.py b/brigade/plugins/tasks/commands/remote_command.py index f7330ae6..47a49a53 100644 --- a/brigade/plugins/tasks/commands/remote_command.py +++ b/brigade/plugins/tasks/commands/remote_command.py @@ -1,12 +1,7 @@ -import logging - from brigade.core.exceptions import CommandError from brigade.core.task import Result -logger = logging.getLogger("brigade") - - def remote_command(task, command): """ Executes a command locally diff --git a/brigade/plugins/tasks/files/sftp.py b/brigade/plugins/tasks/files/sftp.py index c010c5e1..4a51f39d 100644 --- a/brigade/plugins/tasks/files/sftp.py +++ b/brigade/plugins/tasks/files/sftp.py @@ -1,5 +1,4 @@ import hashlib -import logging import os import stat @@ -13,9 +12,6 @@ from scp import SCPClient -logger = logging.getLogger("brigade") - - def get_src_hash(filename): sha1sum = hashlib.sha1() From 027a4407df13553cfac2cf18e0f719d020d6dd77 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Wed, 27 Dec 2017 16:28:39 +0100 Subject: [PATCH 43/67] improvements to logging --- brigade/core/__init__.py | 67 ++++++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 17 deletions(-) diff --git a/brigade/core/__init__.py b/brigade/core/__init__.py index 92e82c4c..8583c77b 100644 --- a/brigade/core/__init__.py +++ b/brigade/core/__init__.py @@ -1,4 +1,5 @@ import logging +import logging.config import sys import traceback from multiprocessing.dummy import Pool @@ -33,9 +34,6 @@ def _unpickle_method(func_name, obj, cls): copy_reg.pickle(types.MethodType, _pickle_method, _unpickle_method) -logger = logging.getLogger("brigade") - - class Brigade(object): """ This is the main object to work with. It contains the inventory and it serves @@ -57,7 +55,9 @@ class Brigade(object): """ def __init__(self, inventory, dry_run, config=None, config_file=None, - available_connections=None): + available_connections=None, logger=None): + self.logger = logger or logging.getLogger("brigade") + self.inventory = inventory self.inventory.brigade = self @@ -67,18 +67,46 @@ def __init__(self, inventory, dry_run, config=None, config_file=None, else: self.config = config or Config() - format = "\033[31m%(asctime)s - %(name)s - %(levelname)s" - format += " - %(funcName)20s() - %(message)s\033[0m" - logging.basicConfig( - level=logging.ERROR, - format=format, - filename="brigade.log", - ) + self.configure_logging() + if available_connections is not None: self.available_connections = available_connections else: self.available_connections = connections.available_connections + def configure_logging(self): + format = "%(asctime)s - %(name)s - %(levelname)s" + format += " - %(funcName)10s() - %(message)s" + logging.config.dictConfig({ + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "simple": {"format": format} + }, + "handlers": { + "info_file_handler": { + "class": "logging.handlers.RotatingFileHandler", + "level": "INFO", + "formatter": "simple", + "filename": "brigade.log", + "maxBytes": 10485760, + "backupCount": 20, + "encoding": "utf8" + }, + }, + "loggers": { + "brigade": { + "level": "INFO", + "handlers": ["info_file_handler"], + "propagate": "no" + }, + }, + "root": { + "level": "ERROR", + "handlers": ["info_file_handler"] + } + }) + def filter(self, **kwargs): """ See :py:meth:`brigade.core.inventory.Inventory.filter` @@ -95,13 +123,14 @@ def _run_serial(self, task, dry_run, **kwargs): result = AggregatedResult() for host in self.inventory.hosts.values(): try: - logger.debug("{}: running task {}".format(host.name, t)) + self.logger.info("{}: {}: running task".format(host.name, t.name)) r = t._start(host=host, brigade=self, dry_run=dry_run) result[host.name] = r except Exception as e: - logger.error("{}: {}".format(host, e)) - result.failed_hosts[host.name] = e - result.tracebacks[host.name] = traceback.format_exc() + tb = traceback.format_exc() + r = Result(host, exception=e, result=tb, failed=True) + result[host.name] = r + self.logger.error("{}: {}".format(host, tb)) return result def _run_parallel(self, task, num_workers, dry_run, **kwargs): @@ -142,6 +171,9 @@ def run(self, task, num_workers=None, dry_run=None, **kwargs): """ num_workers = num_workers or self.config.num_workers + self.logger.info("Running task '{}' with num_workers: {}, dry_run: {}".format( + kwargs.get("name") or task.__name__, num_workers, dry_run)) + self.logger.debug(kwargs) if num_workers == 1: result = self._run_serial(task, dry_run, **kwargs) else: @@ -153,10 +185,11 @@ def run(self, task, num_workers=None, dry_run=None, **kwargs): def run_task(host, brigade, dry_run, task): + logger = logging.getLogger("brigade") try: - logger.debug("{}: running task {}".format(host.name, task)) + logger.info("{}: {}: running task".format(host.name, task.name)) r = task._start(host=host, brigade=brigade, dry_run=dry_run) return host.name, r, None, None except Exception as e: - logger.error("{}: {}".format(host, e)) + logger.error("{}: {}".format(host, traceback.format_exc())) return host.name, None, e, traceback.format_exc() From 5e59282e156b5b786fcf46a71c1892f4505f541e Mon Sep 17 00:00:00 2001 From: David Barroso Date: Wed, 27 Dec 2017 19:42:36 +0100 Subject: [PATCH 44/67] better error handling --- brigade/core/__init__.py | 38 +++++++------------ brigade/core/exceptions.py | 18 ++++----- brigade/core/inventory.py | 4 +- brigade/core/task.py | 27 +++++++------ tests/core/test_multithreading.py | 8 ++-- tests/plugins/tasks/commands/test_command.py | 8 ++-- .../tasks/commands/test_remote_command.py | 4 +- tests/plugins/tasks/data/test_load_json.py | 8 ++-- tests/plugins/tasks/data/test_load_yaml.py | 8 ++-- .../tasks/networking/test_napalm_cli.py | 4 +- .../tasks/networking/test_napalm_configure.py | 4 +- .../tasks/networking/test_napalm_get.py | 4 +- .../plugins/tasks/networking/test_tcp_ping.py | 8 ++-- .../plugins/tasks/text/test_template_file.py | 4 +- .../tasks/text/test_template_string.py | 4 +- 15 files changed, 73 insertions(+), 78 deletions(-) diff --git a/brigade/core/__init__.py b/brigade/core/__init__.py index 8583c77b..9b304723 100644 --- a/brigade/core/__init__.py +++ b/brigade/core/__init__.py @@ -5,7 +5,7 @@ from multiprocessing.dummy import Pool from brigade.core.configuration import Config -from brigade.core.task import AggregatedResult, Task +from brigade.core.task import AggregatedResult, Result, Task from brigade.plugins.tasks import connections @@ -119,36 +119,24 @@ def filter(self, **kwargs): return b def _run_serial(self, task, dry_run, **kwargs): - t = Task(task, **kwargs) - result = AggregatedResult() + result = AggregatedResult(kwargs.get("name") or task.__name__) for host in self.inventory.hosts.values(): - try: - self.logger.info("{}: {}: running task".format(host.name, t.name)) - r = t._start(host=host, brigade=self, dry_run=dry_run) - result[host.name] = r - except Exception as e: - tb = traceback.format_exc() - r = Result(host, exception=e, result=tb, failed=True) - result[host.name] = r - self.logger.error("{}: {}".format(host, tb)) + result[host.name] = run_task(host, self, dry_run, Task(task, **kwargs)) return result def _run_parallel(self, task, num_workers, dry_run, **kwargs): - result = AggregatedResult() + result = AggregatedResult(kwargs.get("name") or task.__name__) pool = Pool(processes=num_workers) - result_pool = [pool.apply_async(run_task, args=(h, self, dry_run, Task(task, **kwargs))) + result_pool = [pool.apply_async(run_task, + args=(h, self, dry_run, Task(task, **kwargs))) for h in self.inventory.hosts.values()] pool.close() pool.join() - for r in result_pool: - host, res, exc, traceback = r.get() - if exc: - result.failed_hosts[host] = exc - result.tracebacks[host] = traceback - else: - result[host] = res + for rp in result_pool: + r = rp.get() + result[r.host.name] = r return result def run(self, task, num_workers=None, dry_run=None, **kwargs): @@ -174,6 +162,7 @@ def run(self, task, num_workers=None, dry_run=None, **kwargs): self.logger.info("Running task '{}' with num_workers: {}, dry_run: {}".format( kwargs.get("name") or task.__name__, num_workers, dry_run)) self.logger.debug(kwargs) + if num_workers == 1: result = self._run_serial(task, dry_run, **kwargs) else: @@ -189,7 +178,8 @@ def run_task(host, brigade, dry_run, task): try: logger.info("{}: {}: running task".format(host.name, task.name)) r = task._start(host=host, brigade=brigade, dry_run=dry_run) - return host.name, r, None, None except Exception as e: - logger.error("{}: {}".format(host, traceback.format_exc())) - return host.name, None, e, traceback.format_exc() + tb = traceback.format_exc() + logger.error("{}: {}".format(host, tb)) + r = Result(host, exception=e, result=tb, failed=True) + return r diff --git a/brigade/core/exceptions.py b/brigade/core/exceptions.py index d16efacd..e19e2532 100644 --- a/brigade/core/exceptions.py +++ b/brigade/core/exceptions.py @@ -32,19 +32,19 @@ class BrigadeExecutionError(Exception): """ def __init__(self, result): self.result = result - self.failed_hosts = result.failed_hosts - self.tracebacks = result.tracebacks + + @property + def failed_hosts(self): + return {k: v for k, v in self.result.items() if v.failed} def __str__(self): text = "\n" for k, r in self.result.items(): text += "{}\n".format("#" * 40) - text += "# {} (succeeded) \n".format(k) - text += "{}\n".format("#" * 40) - text += "{}\n".format(r) - for k, r in self.tracebacks.items(): - text += "{}\n".format("#" * 40) - text += "# {} (failed) \n".format(k) + if r.failed: + text += "# {} (failed)\n".format(k) + else: + text += "# {} (succeeded)\n".format(k) text += "{}\n".format("#" * 40) - text += "{}\n".format(r) + text += "{}\n".format(r.result) return text diff --git a/brigade/core/inventory.py b/brigade/core/inventory.py index adb5186e..b30b85b6 100644 --- a/brigade/core/inventory.py +++ b/brigade/core/inventory.py @@ -214,8 +214,8 @@ def get_connection(self, connection): # we are already inside a thread # Task should establish a connection and populate self.connection[connection] r = self.brigade.filter(name=self.name).run(conn_task, num_workers=1) - if self.name in r.failed_hosts: - raise r.failed_hosts[self.name] + if r[self.name].exception: + raise r[self.name].exception return self.connections[connection] diff --git a/brigade/core/task.py b/brigade/core/task.py index 34006e85..d6018725 100644 --- a/brigade/core/task.py +++ b/brigade/core/task.py @@ -22,20 +22,21 @@ class Task(object): dry_run(``bool``): Populated right before calling the ``task`` """ - def __init__(self, task, **kwargs): + def __init__(self, task, name=None, **kwargs): + self.name = name or task.__name__ self.task = task self.params = kwargs def __repr__(self): - return self.task.__name__ + return self.name def _start(self, host, brigade, dry_run): self.host = host self.brigade = brigade self.dry_run = dry_run if dry_run is not None else brigade.dry_run - return self.task(self, **self.params) + return self.task(self, **self.params) or Result(host) - def run(self, task, **kwargs): + def run(self, task, dry_run=None, **kwargs): """ This is a utility method to call a task from within a task. For instance: @@ -51,8 +52,7 @@ def grouped_tasks(task): msg = ("You have to call this after setting host and brigade attributes. ", "You probably called this from outside a nested task") raise Exception(msg) - aggr = self.brigade.filter(name=self.host.name).run(task, num_workers=1, **kwargs) - return aggr[self.host.name] + return Task(task, **kwargs)._start(self.host, self.brigade, dry_run) class Result(object): @@ -72,11 +72,14 @@ class Result(object): host (:obj:`brigade.core.inventory.Host`): Reference to the host that lead ot this result """ - def __init__(self, host, result=None, changed=False, diff="", **kwargs): + def __init__(self, host, result=None, changed=False, diff="", failed=False, exception=None, + **kwargs): self.result = result self.host = host self.changed = changed self.diff = diff + self.failed = failed + self.exception = exception for k, v in kwargs.items(): setattr(self, k, v) @@ -90,15 +93,17 @@ class AggregatedResult(dict): Attributes: failed_hosts (list): list of hosts that failed """ - def __init__(self, **kwargs): + def __init__(self, name, **kwargs): + self.name = name super().__init__(**kwargs) - self.failed_hosts = {} - self.tracebacks = {} + + def __repr__(self): + return '{}: {}'.format(self.__class__.__name__, self.name) @property def failed(self): """If ``True`` at least a host failed.""" - return bool(self.failed_hosts) + return any([h.failed for h in self.values()]) def raise_on_error(self): """ diff --git a/tests/core/test_multithreading.py b/tests/core/test_multithreading.py index a03e4452..5c334d34 100644 --- a/tests/core/test_multithreading.py +++ b/tests/core/test_multithreading.py @@ -51,28 +51,28 @@ def test_failing_task_simple_singlethread(self, brigade): brigade.run(failing_task_simple, num_workers=1) for k, v in e.value.result.items(): assert isinstance(k, str), k - assert isinstance(v, Exception), v + assert isinstance(v.exception, Exception), v def test_failing_task_simple_multithread(self, brigade): with pytest.raises(BrigadeExecutionError) as e: brigade.run(failing_task_simple, num_workers=NUM_WORKERS) for k, v in e.value.result.items(): assert isinstance(k, str), k - assert isinstance(v, Exception), v + assert isinstance(v.exception, Exception), v def test_failing_task_complex_singlethread(self, brigade): with pytest.raises(BrigadeExecutionError) as e: brigade.run(failing_task_complex, num_workers=1) for k, v in e.value.result.items(): assert isinstance(k, str), k - assert isinstance(v, CommandError), v + assert isinstance(v.exception, CommandError), v def test_failing_task_complex_multithread(self, brigade): with pytest.raises(BrigadeExecutionError) as e: brigade.run(failing_task_complex, num_workers=NUM_WORKERS) for k, v in e.value.result.items(): assert isinstance(k, str), k - assert isinstance(v, CommandError), v + assert isinstance(v.exception, CommandError), v def test_change_data_in_thread(self, brigade): brigade.run(change_data, num_workers=NUM_WORKERS) diff --git a/tests/plugins/tasks/commands/test_command.py b/tests/plugins/tasks/commands/test_command.py index 312d559e..7ed7e6cd 100644 --- a/tests/plugins/tasks/commands/test_command.py +++ b/tests/plugins/tasks/commands/test_command.py @@ -18,13 +18,13 @@ def test_command_error(self, brigade): brigade.run(commands.command, command="ech") assert len(e.value.failed_hosts) == len(brigade.inventory.hosts) - for exc in e.value.failed_hosts.values(): - assert isinstance(exc, OSError) + for result in e.value.failed_hosts.values(): + assert isinstance(result.exception, OSError) def test_command_error_generic(self, brigade): with pytest.raises(BrigadeExecutionError) as e: brigade.run(commands.command, command="ls /asdadsd") assert len(e.value.failed_hosts) == len(brigade.inventory.hosts) - for exc in e.value.failed_hosts.values(): - assert isinstance(exc, CommandError) + for result in e.value.failed_hosts.values(): + assert isinstance(result.exception, CommandError) diff --git a/tests/plugins/tasks/commands/test_remote_command.py b/tests/plugins/tasks/commands/test_remote_command.py index 63d3b85f..9486745f 100644 --- a/tests/plugins/tasks/commands/test_remote_command.py +++ b/tests/plugins/tasks/commands/test_remote_command.py @@ -18,5 +18,5 @@ def test_remote_command_error_generic(self, brigade): brigade.run(commands.remote_command, command="ls /asdadsd") assert len(e.value.failed_hosts) == len(brigade.inventory.hosts) - for exc in e.value.failed_hosts.values(): - assert isinstance(exc, CommandError) + for result in e.value.failed_hosts.values(): + assert isinstance(result.exception, CommandError) diff --git a/tests/plugins/tasks/data/test_load_json.py b/tests/plugins/tasks/data/test_load_json.py index 03e767ec..e53d778b 100644 --- a/tests/plugins/tasks/data/test_load_json.py +++ b/tests/plugins/tasks/data/test_load_json.py @@ -29,8 +29,8 @@ def test_load_json_error_broken_file(self, brigade): brigade.run(data.load_json, file=test_file) assert len(e.value.failed_hosts) == len(brigade.inventory.hosts) - for exc in e.value.failed_hosts.values(): - assert isinstance(exc, ValueError) + for result in e.value.failed_hosts.values(): + assert isinstance(result.exception, ValueError) def test_load_json_error_missing_file(self, brigade): test_file = '{}/missing.json'.format(data_dir) @@ -43,5 +43,5 @@ def test_load_json_error_missing_file(self, brigade): brigade.run(data.load_json, file=test_file) assert len(e.value.failed_hosts) == len(brigade.inventory.hosts) - for exc in e.value.failed_hosts.values(): - assert isinstance(exc, not_found) + for result in e.value.failed_hosts.values(): + assert isinstance(result.exception, not_found) diff --git a/tests/plugins/tasks/data/test_load_yaml.py b/tests/plugins/tasks/data/test_load_yaml.py index 15fbde6b..4421cb96 100644 --- a/tests/plugins/tasks/data/test_load_yaml.py +++ b/tests/plugins/tasks/data/test_load_yaml.py @@ -33,8 +33,8 @@ def test_load_yaml_error_broken_file(self, brigade): brigade.run(data.load_yaml, file=test_file) assert len(e.value.failed_hosts) == len(brigade.inventory.hosts) - for exc in e.value.failed_hosts.values(): - assert isinstance(exc, ScannerError) + for result in e.value.failed_hosts.values(): + assert isinstance(result.exception, ScannerError) def test_load_yaml_error_missing_file(self, brigade): test_file = '{}/missing.yaml'.format(data_dir) @@ -48,5 +48,5 @@ def test_load_yaml_error_missing_file(self, brigade): brigade.run(data.load_yaml, file=test_file) assert len(e.value.failed_hosts) == len(brigade.inventory.hosts) - for exc in e.value.failed_hosts.values(): - assert isinstance(exc, not_found) + for result in e.value.failed_hosts.values(): + assert isinstance(result.exception, not_found) diff --git a/tests/plugins/tasks/networking/test_napalm_cli.py b/tests/plugins/tasks/networking/test_napalm_cli.py index 4eec82b5..7bc712ff 100644 --- a/tests/plugins/tasks/networking/test_napalm_cli.py +++ b/tests/plugins/tasks/networking/test_napalm_cli.py @@ -34,6 +34,6 @@ def test_napalm_cli(self, brigade): # "show interfacesa"], # optional_args=opt) # assert len(e.value.failed_hosts) - # for exc in e.value.failed_hosts.values(): - # assert isinstance(exc, exceptions.CommandErrorException) + # for result in e.value.failed_hosts.values(): + # assert isinstance(result.exception, exceptions.CommandErrorException) # print(exc) diff --git a/tests/plugins/tasks/networking/test_napalm_configure.py b/tests/plugins/tasks/networking/test_napalm_configure.py index 7d9cf0c7..fcbe0e9d 100644 --- a/tests/plugins/tasks/networking/test_napalm_configure.py +++ b/tests/plugins/tasks/networking/test_napalm_configure.py @@ -55,5 +55,5 @@ def test_napalm_configure_change_error(self, brigade): with pytest.raises(BrigadeExecutionError) as e: d.run(networking.napalm_configure, configuration=configuration) assert len(e.value.failed_hosts) - for exc in e.value.failed_hosts.values(): - assert isinstance(exc, exceptions.MergeConfigException) + for result in e.value.failed_hosts.values(): + assert isinstance(result.exception, exceptions.MergeConfigException) diff --git a/tests/plugins/tasks/networking/test_napalm_get.py b/tests/plugins/tasks/networking/test_napalm_get.py index d3520bb1..459e3933 100644 --- a/tests/plugins/tasks/networking/test_napalm_get.py +++ b/tests/plugins/tasks/networking/test_napalm_get.py @@ -33,5 +33,5 @@ def test_napalm_getters_error(self, brigade): getters=["facts", "interfaces"]) assert len(e.value.failed_hosts) - for exc in e.value.failed_hosts.values(): - assert isinstance(exc, KeyError) + for result in e.value.failed_hosts.values(): + assert isinstance(result.exception, KeyError) diff --git a/tests/plugins/tasks/networking/test_tcp_ping.py b/tests/plugins/tasks/networking/test_tcp_ping.py index 0699e775..eb42993a 100644 --- a/tests/plugins/tasks/networking/test_tcp_ping.py +++ b/tests/plugins/tasks/networking/test_tcp_ping.py @@ -37,16 +37,16 @@ def test_tcp_ping_invalid_port(self, brigade): brigade.run(networking.tcp_ping, ports='web') assert len(e.value.failed_hosts) == len(brigade.inventory.hosts) - for exc in e.value.failed_hosts.values(): - assert isinstance(exc, ValueError) + for result in e.value.failed_hosts.values(): + assert isinstance(result.exception, ValueError) def test_tcp_ping_invalid_ports(self, brigade): with pytest.raises(BrigadeExecutionError) as e: brigade.run(networking.tcp_ping, ports=[22, 'web', 443]) assert len(e.value.failed_hosts) == len(brigade.inventory.hosts) - for exc in e.value.failed_hosts.values(): - assert isinstance(exc, ValueError) + for result in e.value.failed_hosts.values(): + assert isinstance(result.exception, ValueError) def test_tcp_ping_external_hosts(): diff --git a/tests/plugins/tasks/text/test_template_file.py b/tests/plugins/tasks/text/test_template_file.py index ba17d60e..b7aa0070 100644 --- a/tests/plugins/tasks/text/test_template_file.py +++ b/tests/plugins/tasks/text/test_template_file.py @@ -33,5 +33,5 @@ def test_template_file_error_broken_file(self, brigade): template='broken.j2', path=data_dir) assert len(e.value.failed_hosts) == len(brigade.inventory.hosts) - for exc in e.value.failed_hosts.values(): - assert isinstance(exc, TemplateSyntaxError) + for result in e.value.failed_hosts.values(): + assert isinstance(result.exception, TemplateSyntaxError) diff --git a/tests/plugins/tasks/text/test_template_string.py b/tests/plugins/tasks/text/test_template_string.py index 52aef9b9..6919517a 100644 --- a/tests/plugins/tasks/text/test_template_string.py +++ b/tests/plugins/tasks/text/test_template_string.py @@ -49,5 +49,5 @@ def test_template_string_error_broken_string(self, brigade): brigade.run(text.template_string, template=broken_j2) assert len(e.value.failed_hosts) == len(brigade.inventory.hosts) - for exc in e.value.failed_hosts.values(): - assert isinstance(exc, TemplateSyntaxError) + for result in e.value.failed_hosts.values(): + assert isinstance(result.exception, TemplateSyntaxError) From 52f63f9d1c75ed773993e1893b1af019004f7fdb Mon Sep 17 00:00:00 2001 From: David Barroso Date: Wed, 27 Dec 2017 19:52:20 +0100 Subject: [PATCH 45/67] document new Result attributes --- brigade/core/task.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/brigade/core/task.py b/brigade/core/task.py index d6018725..553ff367 100644 --- a/brigade/core/task.py +++ b/brigade/core/task.py @@ -64,12 +64,16 @@ class Result(object): diff (obj): Diff between state of the system before/after running this task result (obj): Result of the task execution, see task's documentation for details host (:obj:`brigade.core.inventory.Host`): Reference to the host that lead ot this result + failed (bool): Whether the execution failed or not + exception (Exception): uncaught exception thrown during the exection of the task (if any) Attributes: changed (bool): ``True`` if the task is changing the system diff (obj): Diff between state of the system before/after running this task result (obj): Result of the task execution, see task's documentation for details host (:obj:`brigade.core.inventory.Host`): Reference to the host that lead ot this result + failed (bool): Whether the execution failed or not + exception (Exception): uncaught exception thrown during the exection of the task (if any) """ def __init__(self, host, result=None, changed=False, diff="", failed=False, exception=None, From 4972edd6aa8ced42c5f9bed881a21bc5b9d07367 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Fri, 29 Dec 2017 18:55:35 +0100 Subject: [PATCH 46/67] allow overriding raise_on_error per task For instance: result = d.run(networking.napalm_validate, raise_on_error=False, src=THIS_DIR + "/data/validate_error.yaml") --- brigade/core/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/brigade/core/__init__.py b/brigade/core/__init__.py index 9b304723..e1cd473c 100644 --- a/brigade/core/__init__.py +++ b/brigade/core/__init__.py @@ -139,7 +139,7 @@ def _run_parallel(self, task, num_workers, dry_run, **kwargs): result[r.host.name] = r return result - def run(self, task, num_workers=None, dry_run=None, **kwargs): + def run(self, task, num_workers=None, dry_run=None, raise_on_error=None, **kwargs): """ Run task over all the hosts in the inventory. @@ -148,6 +148,7 @@ def run(self, task, num_workers=None, dry_run=None, **kwargs): the inventory num_workers(``int``): Override for how many hosts to run in paralell for this task dry_run(``bool``): Whether if we are testing the changes or not + raise_on_error (``bool``): Override raise_on_error behavior **kwargs: additional argument to pass to ``task`` when calling it Raises: @@ -168,7 +169,9 @@ def run(self, task, num_workers=None, dry_run=None, **kwargs): else: result = self._run_parallel(task, num_workers, dry_run, **kwargs) - if self.config.raise_on_error: + raise_on_error = raise_on_error if raise_on_error is not None else \ + self.config.raise_on_error + if raise_on_error: result.raise_on_error() return result From cecc260724cd9cebcb3dc30cdc219388681b6b34 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Fri, 29 Dec 2017 19:13:37 +0100 Subject: [PATCH 47/67] added napalm_validate task --- brigade/plugins/tasks/networking/__init__.py | 2 + .../tasks/networking/napalm_validate.py | 22 +++++++++ .../tasks/networking/data/validate_error.yaml | 6 +++ .../tasks/networking/data/validate_ok.yaml | 6 +++ .../tasks/networking/test_napalm_validate.py | 49 +++++++++++++++++++ 5 files changed, 85 insertions(+) create mode 100644 brigade/plugins/tasks/networking/napalm_validate.py create mode 100644 tests/plugins/tasks/networking/data/validate_error.yaml create mode 100644 tests/plugins/tasks/networking/data/validate_ok.yaml create mode 100644 tests/plugins/tasks/networking/test_napalm_validate.py diff --git a/brigade/plugins/tasks/networking/__init__.py b/brigade/plugins/tasks/networking/__init__.py index 2b62194e..59e33ed9 100644 --- a/brigade/plugins/tasks/networking/__init__.py +++ b/brigade/plugins/tasks/networking/__init__.py @@ -1,6 +1,7 @@ from .napalm_cli import napalm_cli from .napalm_configure import napalm_configure from .napalm_get import napalm_get +from .napalm_validate import napalm_validate from .netmiko_send_command import netmiko_send_command from .tcp_ping import tcp_ping @@ -8,6 +9,7 @@ "napalm_cli", "napalm_configure", "napalm_get", + "napalm_validate", "netmiko_send_command", "tcp_ping", ) diff --git a/brigade/plugins/tasks/networking/napalm_validate.py b/brigade/plugins/tasks/networking/napalm_validate.py new file mode 100644 index 00000000..76e9ab9d --- /dev/null +++ b/brigade/plugins/tasks/networking/napalm_validate.py @@ -0,0 +1,22 @@ +from brigade.core.task import Result + + +def napalm_validate(task, src=None, validation_source=None): + """ + Gather information with napalm and validate it: + + http://napalm.readthedocs.io/en/develop/validate/index.html + + Arguments: + src: file to use as validation source + validation_source (list): instead of a file data needed to validate device's state + + Returns: + :obj:`brigade.core.task.Result`: + * result (``dict``): dictionary with the result of the validation + * failed (``bool``): Whether the device complies or not (note this will trigger a + :obj:`brigade.core.exceptions.BrigadeExecutionError` if raise_on_error is set to ``True``) + """ + device = task.host.get_connection("napalm") + r = device.compliance_report(validation_file=src, validation_source=validation_source) + return Result(host=task.host, result=r, failed=not r["complies"]) diff --git a/tests/plugins/tasks/networking/data/validate_error.yaml b/tests/plugins/tasks/networking/data/validate_error.yaml new file mode 100644 index 00000000..9ecfe461 --- /dev/null +++ b/tests/plugins/tasks/networking/data/validate_error.yaml @@ -0,0 +1,6 @@ +--- +- get_facts: + os_version: 4.15.5M-3054042.4155M +- get_interfaces: + Ethernet1: + description: "unset" diff --git a/tests/plugins/tasks/networking/data/validate_ok.yaml b/tests/plugins/tasks/networking/data/validate_ok.yaml new file mode 100644 index 00000000..68d68381 --- /dev/null +++ b/tests/plugins/tasks/networking/data/validate_ok.yaml @@ -0,0 +1,6 @@ +--- +- get_facts: + os_version: 4.15.5M-3054042.4155M +- get_interfaces: + Ethernet1: + description: "" diff --git a/tests/plugins/tasks/networking/test_napalm_validate.py b/tests/plugins/tasks/networking/test_napalm_validate.py new file mode 100644 index 00000000..ed899066 --- /dev/null +++ b/tests/plugins/tasks/networking/test_napalm_validate.py @@ -0,0 +1,49 @@ +import os + +from brigade.plugins.tasks import connections, networking + + +THIS_DIR = os.path.dirname(os.path.realpath(__file__)) + + +class Test(object): + + def test_napalm_validate_src_ok(self, brigade): + opt = {"path": THIS_DIR + "/mocked/napalm_get/test_napalm_getters"} + print(opt["path"]) + d = brigade.filter(name="dev3.group_2") + d.run(connections.napalm_connection, optional_args=opt) + result = d.run(networking.napalm_validate, + src=THIS_DIR + "/data/validate_ok.yaml") + assert result + for h, r in result.items(): + assert not r.failed + + def test_napalm_validate_src_error(self, brigade): + opt = {"path": THIS_DIR + "/mocked/napalm_get/test_napalm_getters"} + print(opt["path"]) + d = brigade.filter(name="dev3.group_2") + d.run(connections.napalm_connection, optional_args=opt) + result = d.run(networking.napalm_validate, + src=THIS_DIR + "/data/validate_error.yaml") + assert result + for h, r in result.items(): + assert r.failed + assert not r.result["complies"] + + def test_napalm_validate_src_validate_source(self, brigade): + opt = {"path": THIS_DIR + "/mocked/napalm_get/test_napalm_getters"} + print(opt["path"]) + d = brigade.filter(name="dev3.group_2") + d.run(connections.napalm_connection, optional_args=opt) + + validation_dict = [ + {"get_interfaces": {"Ethernet1": {"description": ""}}} + ] + + result = d.run(networking.napalm_validate, + validation_source=validation_dict) + + assert result + for h, r in result.items(): + assert not r.failed From 30e8570e77b3da4257126e2247ddd5ab2a587069 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Fri, 29 Dec 2017 19:14:35 +0100 Subject: [PATCH 48/67] render automatically template path --- brigade/plugins/tasks/text/template_file.py | 1 + 1 file changed, 1 insertion(+) diff --git a/brigade/plugins/tasks/text/template_file.py b/brigade/plugins/tasks/text/template_file.py index 66207780..9b8d8a78 100644 --- a/brigade/plugins/tasks/text/template_file.py +++ b/brigade/plugins/tasks/text/template_file.py @@ -17,6 +17,7 @@ def template_file(task, template, path, **kwargs): """ merged = merge_two_dicts(task.host, kwargs) path = format_string(path, task, **kwargs) + template = format_string(template, task, **kwargs) text = jinja_helper.render_from_file(template=template, path=path, host=task.host, **merged) return Result(host=task.host, result=text) From 0b265ce984474acbfe9691a91aefcd49ed511fc1 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Fri, 29 Dec 2017 19:19:30 +0100 Subject: [PATCH 49/67] Added helper functions to make output pretty --- brigade/plugins/functions/__init__.py | 6 +++ brigade/plugins/functions/text/__init__.py | 12 ++++++ brigade/plugins/tasks/text/__init__.py | 2 + brigade/plugins/tasks/text/print_result.py | 45 ++++++++++++++++++++++ requirements.txt | 1 + 5 files changed, 66 insertions(+) create mode 100644 brigade/plugins/functions/__init__.py create mode 100644 brigade/plugins/functions/text/__init__.py create mode 100644 brigade/plugins/tasks/text/print_result.py diff --git a/brigade/plugins/functions/__init__.py b/brigade/plugins/functions/__init__.py new file mode 100644 index 00000000..419dd70e --- /dev/null +++ b/brigade/plugins/functions/__init__.py @@ -0,0 +1,6 @@ +from . import text + + +__all__ = ( + "text", +) diff --git a/brigade/plugins/functions/text/__init__.py b/brigade/plugins/functions/text/__init__.py new file mode 100644 index 00000000..b7a96615 --- /dev/null +++ b/brigade/plugins/functions/text/__init__.py @@ -0,0 +1,12 @@ +from colorama import Fore, Style, init + + +init(autoreset=True) + + +def print_title(title): + """ + Helper function to print a title. + """ + msg = "**** {} ".format(title) + print("{}{}{}{}".format(Style.BRIGHT, Fore.GREEN, msg, "*" * (80 - len(msg)))) diff --git a/brigade/plugins/tasks/text/__init__.py b/brigade/plugins/tasks/text/__init__.py index dfc5efa3..3a41db10 100644 --- a/brigade/plugins/tasks/text/__init__.py +++ b/brigade/plugins/tasks/text/__init__.py @@ -1,7 +1,9 @@ +from .print_result import print_result from .template_file import template_file from .template_string import template_string __all__ = ( + "print_result", "template_file", "template_string", ) diff --git a/brigade/plugins/tasks/text/print_result.py b/brigade/plugins/tasks/text/print_result.py new file mode 100644 index 00000000..337b1018 --- /dev/null +++ b/brigade/plugins/tasks/text/print_result.py @@ -0,0 +1,45 @@ +import pprint + +from brigade.core.task import AggregatedResult, Result + +from colorama import Fore, Style, init + + +init(autoreset=True) + + +def print_result(task, data, vars=None): + """ + Prints on screen the :obj:`brigade.core.task.Result` from a previous task + + Arguments: + data (:obj:`brigade.core.task.Result`): from a previous task + vars (list of str): Which attributes you want to print + + Returns: + :obj:`brigade.core.task.Result`: + """ + vars = vars or ["diff", "result", "stdout"] + if isinstance(vars, str): + vars = [vars] + + if isinstance(data, AggregatedResult): + data = data[task.host.name] + + if data.failed: + color = Fore.RED + elif data.changed: + color = Fore.YELLOW + else: + color = Fore.BLUE + changed = "" if data.changed is None else " ** changed : {} ".format(data.changed) + msg = "* {}{}".format(task.host.name, changed) + print("{}{}{}{}".format(Style.BRIGHT, color, msg, "*" * (80 - len(msg)))) + for v in vars: + r = getattr(data, v, "") + if r and not isinstance(r, str): + pprint.pprint(r) + elif r: + print(r) + + return Result(task.host) diff --git a/requirements.txt b/requirements.txt index 235d09d0..2efafbfc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +colorama pyyaml jinja2 napalm>=2.2.0 From 3e89f8c65560a0cacb26ba2943c0e49d9226407b Mon Sep 17 00:00:00 2001 From: David Barroso Date: Fri, 29 Dec 2017 19:24:16 +0100 Subject: [PATCH 50/67] fix napalm_validate tests --- tests/inventory_data/nsot/nsot.sqlite3 | Bin 228352 -> 245760 bytes .../tasks/networking/test_napalm_validate.py | 1 + 2 files changed, 1 insertion(+) diff --git a/tests/inventory_data/nsot/nsot.sqlite3 b/tests/inventory_data/nsot/nsot.sqlite3 index 5fd8479baee4161f562baafe6f0233a43985d474..a410603ce9a25489080862fa4ad1b2a65b5defc3 100644 GIT binary patch delta 6818 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 Sj$aX21h!4>`fondu=zir1EXL7 delta 733 zcmZuuOH5Ny5WO?=fI?ajMQj2|YviMbl$Q6>7F1N?0xCjcWMjH`{2^7UiJ;56&{Rbz zVlskmMB+vyn(BKI65JUV$O0uwNF}&(p&J(p#Taj)8xxb8n>#c2%$aj063L6K+hRo- znh;`j#Ca67tNKeFCc7;q)F^Xem90HcAJ4DYM6)>rhsyIkPPey4_0_0umtWO1t;)QA z2YPq!P$-xJyr+*eO+mUQF((OL^H6|F6P1iGOp6` z;Aq6!!a*GaJfFG_w@SJaIt9G&1)ZSsVk?F@-iDX>Vml7=$`dH%)(&jq4;>gQ%aJs8 z5f!rKqWA^Ha9l*83vR(PSb`)v@C5ea5bxJflSD4Q-Gv8up^BWm-ihOEcHs*i>BexH z@~osR;;D6PX6FVr@X!W^*pbA5(Su5brC2-=^2~Bh3}f7UgR=PREadUrb+nkvcd?wd z6!Y9pvVh;Xhl+0B1$6@Ipc@9^`Tu76!OKah@cbr@vQLUjPtr%ujNWY@7SYK+>#3h( z4b;c`0u(ZS<2$$ct%3?9VI|qKe45we@tiT-lxerr3Q;TCMRd#KTslW1WYCHb+uiDD zFAomTD-K Date: Sun, 31 Dec 2017 16:13:06 +0100 Subject: [PATCH 51/67] bugfix --- brigade/core/helpers/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brigade/core/helpers/__init__.py b/brigade/core/helpers/__init__.py index 652792b4..67514857 100644 --- a/brigade/core/helpers/__init__.py +++ b/brigade/core/helpers/__init__.py @@ -2,7 +2,7 @@ def merge_two_dicts(x, y): try: z = x.copy() except AttributeError: - z = x.items() + z = dict(x) z.update(y) return z From ca49110704ac7c731e3a300dfd56ebeda12b39d0 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Sun, 31 Dec 2017 16:13:37 +0100 Subject: [PATCH 52/67] return properly dict_keys and dict_values --- brigade/core/inventory.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/brigade/core/inventory.py b/brigade/core/inventory.py index b30b85b6..da7af8fe 100644 --- a/brigade/core/inventory.py +++ b/brigade/core/inventory.py @@ -73,19 +73,17 @@ def __init__(self, name, group=None, brigade=None, **kwargs): for k, v in kwargs.items(): self.data[k] = v + def _resolve_data(self): + d = self.group if self.group else {} + return helpers.merge_two_dicts(d, self.data) + def keys(self): """Returns the keys of the attribute ``data`` and of the parent(s) groups.""" - k = list(self.data.keys()) - if self.group: - k.extend(list(self.group.keys())) - return k + return self._resolve_data().keys() def values(self): """Returns the values of the attribute ``data`` and of the parent(s) groups.""" - v = list(self.data.values()) - if self.group: - v.extend(list(self.group.values())) - return v + return self._resolve_data().values() def __getitem__(self, item): try: @@ -128,11 +126,7 @@ def items(self): Returns all the data accessible from a device, including the one inherited from parent groups """ - if self.group: - d = self.group.items() - else: - d = {} - return helpers.merge_two_dicts(d, self.data) + return self._resolve_data().items() @property def brigade(self): From 1e06a18fd8e7bfe7c983824d60352a89c5a25e52 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Sat, 6 Jan 2018 13:27:49 +0100 Subject: [PATCH 53/67] subtasks now return MultiResults which aggregate mutiple results for the same host --- brigade/core/__init__.py | 4 +- brigade/core/task.py | 55 +++++++++++++++++++--- brigade/plugins/tasks/text/print_result.py | 20 ++++---- tests/core/test_tasks.py | 8 ++-- 4 files changed, 68 insertions(+), 19 deletions(-) diff --git a/brigade/core/__init__.py b/brigade/core/__init__.py index e1cd473c..1f24293b 100644 --- a/brigade/core/__init__.py +++ b/brigade/core/__init__.py @@ -185,4 +185,6 @@ def run_task(host, brigade, dry_run, task): tb = traceback.format_exc() logger.error("{}: {}".format(host, tb)) r = Result(host, exception=e, result=tb, failed=True) - return r + task.results.append(r) + + return task.results diff --git a/brigade/core/task.py b/brigade/core/task.py index 553ff367..cdff5290 100644 --- a/brigade/core/task.py +++ b/brigade/core/task.py @@ -26,15 +26,23 @@ def __init__(self, task, name=None, **kwargs): self.name = name or task.__name__ self.task = task self.params = kwargs + self.results = MultiResult(self.name) def __repr__(self): return self.name - def _start(self, host, brigade, dry_run): + def _start(self, host, brigade, dry_run, sub_task=False): self.host = host self.brigade = brigade self.dry_run = dry_run if dry_run is not None else brigade.dry_run - return self.task(self, **self.params) or Result(host) + r = self.task(self, **self.params) or Result(host) + r.name = self.name + + if sub_task: + return r + else: + self.results.insert(0, r) + return self.results def run(self, task, dry_run=None, **kwargs): """ @@ -52,12 +60,18 @@ def grouped_tasks(task): msg = ("You have to call this after setting host and brigade attributes. ", "You probably called this from outside a nested task") raise Exception(msg) - return Task(task, **kwargs)._start(self.host, self.brigade, dry_run) + r = Task(task, **kwargs)._start(self.host, self.brigade, dry_run, sub_task=True) + + if isinstance(r, MultiResult): + self.results.extend(r) + else: + self.results.append(r) + return r class Result(object): """ - Returned by tasks. + Result of running individual tasks. Arguments: changed (bool): ``True`` if the task is changing the system @@ -93,9 +107,6 @@ class AggregatedResult(dict): """ It basically is a dict-like object that aggregates the results for all devices. You can access each individual result by doing ``my_aggr_result["hostname_of_device"]``. - - Attributes: - failed_hosts (list): list of hosts that failed """ def __init__(self, name, **kwargs): self.name = name @@ -116,3 +127,33 @@ def raise_on_error(self): """ if self.failed: raise BrigadeExecutionError(self) + + +class MultiResult(list): + """ + It is basically is a list-like object that gives you access to the results of all subtasks for + a particular device/task. + """ + def __init__(self, name): + self.name = name + + def __getattr__(self, name): + return getattr(self[0], name) + + @property + def failed(self): + """If ``True`` at least a task failed.""" + return any([h.failed for h in self]) + + @property + def changed(self): + """If ``True`` at least a task changed the system.""" + return any([h.changed for h in self]) + + def raise_on_error(self): + """ + Raises: + :obj:`brigade.core.exceptions.BrigadeExecutionError`: When at least a task failed + """ + if self.failed: + raise BrigadeExecutionError(self) diff --git a/brigade/plugins/tasks/text/print_result.py b/brigade/plugins/tasks/text/print_result.py index 337b1018..32d19248 100644 --- a/brigade/plugins/tasks/text/print_result.py +++ b/brigade/plugins/tasks/text/print_result.py @@ -32,14 +32,18 @@ def print_result(task, data, vars=None): color = Fore.YELLOW else: color = Fore.BLUE - changed = "" if data.changed is None else " ** changed : {} ".format(data.changed) - msg = "* {}{}".format(task.host.name, changed) + title = "" if data.changed is None else " ** changed : {} ".format(data.changed) + msg = "* {}{}".format(task.host.name, title) print("{}{}{}{}".format(Style.BRIGHT, color, msg, "*" * (80 - len(msg)))) - for v in vars: - r = getattr(data, v, "") - if r and not isinstance(r, str): - pprint.pprint(r) - elif r: - print(r) + for r in data: + subtitle = "" if r.changed is None else " ** changed : {} ".format(r.changed) + msg = "---- {}{} ".format(r.name, subtitle) + print("{}{}{}{}".format(Style.BRIGHT, Fore.CYAN, msg, "-" * (80 - len(msg)))) + for v in vars: + x = getattr(r, v, "") + if r and not isinstance(x, str): + pprint.pprint(x) + elif r: + print(x) return Result(task.host) diff --git a/tests/core/test_tasks.py b/tests/core/test_tasks.py index 93a942ea..8487ca06 100644 --- a/tests/core/test_tasks.py +++ b/tests/core/test_tasks.py @@ -2,8 +2,8 @@ def sub_task(task): - return task.run(commands.command, - command="echo {}".format(task.host)) + task.run(commands.command, + command="echo {}".format(task.host)) class Test(object): @@ -19,4 +19,6 @@ def test_sub_task(self, brigade): result = brigade.run(sub_task) assert result for h, r in result.items(): - assert h == r.stdout.strip() + assert r[0].name == "sub_task" + assert r[1].name == "command" + assert h == r[1].stdout.strip() From ac036f1c30dbe7b23ea10821d7798cc9bbc9f2a0 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Sat, 6 Jan 2018 13:43:34 +0100 Subject: [PATCH 54/67] document how the execution model works (#60) --- docs/ref/index.rst | 6 +++++ .../internals/_static/execution_model.graffle | Bin 0 -> 14903 bytes .../internals/_static/execution_model_1.png | Bin 0 -> 369085 bytes .../internals/_static/execution_model_2.png | Bin 0 -> 371519 bytes docs/ref/internals/execution_model.rst | 22 ++++++++++++++++++ docs/ref/internals/index.rst | 7 ++++++ 6 files changed, 35 insertions(+) create mode 100644 docs/ref/internals/_static/execution_model.graffle create mode 100644 docs/ref/internals/_static/execution_model_1.png create mode 100644 docs/ref/internals/_static/execution_model_2.png create mode 100644 docs/ref/internals/execution_model.rst create mode 100644 docs/ref/internals/index.rst diff --git a/docs/ref/index.rst b/docs/ref/index.rst index e22b5322..7b3d72bb 100644 --- a/docs/ref/index.rst +++ b/docs/ref/index.rst @@ -1,6 +1,12 @@ Reference Guides ================ +.. toctree:: + :maxdepth: 2 + :caption: Brigade Internals + + internals/index + .. toctree:: :maxdepth: 2 :caption: Brigade API diff --git a/docs/ref/internals/_static/execution_model.graffle b/docs/ref/internals/_static/execution_model.graffle new file mode 100644 index 0000000000000000000000000000000000000000..5f0ed3bdd6010fe2617bab59b3842ca7d4a7f3fe GIT binary patch literal 14903 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

%yPE3(?a`JJG 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�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

KnTpUbho%9ouwS>K|JM<6?q`T? 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

i^~ZVm$lU$?pxrf+2hxso?*h z6iPNRtg2fJ=K?v)sBtcgkyZ7pAZUH7{x<8L^e0b5QhGz`vW=@PX+qpIeJv8iD2ZCG zhE;S02Rpqf+F?DMrAjn+s^SlLD(K}rREI0;*c<$4@UGixI>fZ99r)5U5(hm*F>l&liYP9EjTT$O~G%BjJ z_4Ilka_W*IEP1l|%q}Yb4&!a+D-}I%#1uq4O2#DO zOYfC8uajICLq<@MxmqnsUjrEv**kSDkpkYu5++UV6SBH>`)(nt!?M~J#t!$j`tax) zi|v@Pp#$fMZ-#yNbVuH6uosLW&wlm-NH~-1b$_aVdHn98R0`rCkNM+*N3FzmlGNBA zh8RmCxvTMAPwIoC*DsTZ>k0OjxG}C|5&B@5OC=m3XFtd8K1wv=ls^K&wsMc-V~=eX z_0p&hT;j!`Au{9@56P(u%C@??(u{q}%S1xUw@9^d8?fz=yc@sjXB!eaDoJgeABGh} z?tOP>yeB`f75BZWBz-1WoTH*_mVdg4Wa_>*6pD`=f3nnH^Ds@PtN2;>k_Nsm!NI?=nQGy5Q}8jx zmq-4r?4Fp#a{MM*oqh6HYRKjG59F*&CAXe%`DYu@-%gD4Ocov^1>>wEYqurmdUDpQ ztil9dHRi|wnWfn-ca%foLhcFjDclDkZ=kv9CyvSPj}oF}LcRHDrP>V1(6^;>N% z!mlbn=0+X+&Z(TXr8eYB}3jq5q-9ES@6zp1KrUwRc>yw()d9iy-3-^m6o?<=0FRNCD%SZV4Ut10o3_&Ez-Pb_^FtW0e9ASD&b*MUx zvaa2Tr~)n>IN$H=K2Y%b_~L6y*{*pX+j%XqzUmm<3Rp*NIt zQ2`iE@-xHzB3=HOzb6q?U=lcjZ|-Jl>_c|p8J2y6j^8pPO=R&|T4Y&lj_UeVE(6I(w3 zqF?5ABW^ztdJG+{NUKC>U^DRSApeC*>L6lx{Ei&&4Vt2&!b1mm~X1HUlI*nc>D0MEl zW2?)x>kaeg-=!Q#mOR;UYKmK`vcExrA1&pBEp`@OD2BJ#67gkUAw#Peh|;nRYL#nq zGy5^^v!p|-)7E1eW&=_bXUC1nBMdj37=&0Juo7sJbDRpT>WtSjq8^~~=kz{I8mgtc z+_$dwP9?bas2LjifGH)vn6{8njl}h!MzHojFf~$`$uu}$p$&ia>)CZpv{t& z@u*&_&UlLTc!BSkhWKlRhT@{Q755$fSRiNX;0h63NI_#OWX=~7t5`vL%zZq(ZrFVR zGjb##f-o^km!z zOPnHC6R9t1Kw3%9bG5Rw1lEg?Y}8cj)HG*%LIEmgOEuSA72LO+?{j_ z1)#8}jaH-9>000lJy$`4Vgs(kw zYVmZiquds(FWr%gNZ@SwhZ_8fr2P4BYK#sX*qK}`F#_SbKK?+;?Ui+i+t$otniZN) z#Tvi>m^f^Z9C`nsErJ{TO?q7>T3}F}kZV*Gp`3geSbIGFeERYTzol7!>F26U`}!oV zmX~TDb#@B;J}`L$9i+Qu4L8EwjH`D*r%{ZS1EjvBJe@bQ9pW5%Xc@uqA4z<%C(}5j z(GPwu)_CCY(Vki9+gxVh;L9lv26g+EA>qXLg~=sda+D)-1{)CYW!X((faE`zENxiP z9@W*wyPNGfc4U>(5%r4E!}12ZQH{g*K0uy7@Yvs)oz0Bph)|P5bm!QOEHR%X7NjM| zcjnE86Y=$~%ZP7GK)fEUu(#?44#$=0b-*hfzVnj*2pvK$d>ZKhQbaBt#Fld4TE2r) z@Mp3t{vgam~*RNiOdh%PopGE`308*e9?BzX`s68Iu3z z-Kv#t--BGRH!%y>==b9lhlYKfZ6QCkTVngalV%8= zll6Xrm)gr{ia?pQj9Z-z#O1HUcTu_mtO@;yW3#G-A#x%&``i7s8t`*E+3oiF4Ci@F zj4)X_UsuIegnu{|Hg&`e$sI2-dy>m7tzf*CTtsf;qc>azUIu7_YFU)zC)R85QfHGL+Z{cRDHZyG2wj})GB;fI?jrYlRLxqc zOpRO9`B=jxHc6+^2vU^t>To~WHB&Jd5>v_~z+$i^rG?Ha?<_IX7pbz$HwvN&q4Eu( zdefY#miRuFxLlDykL@c)&v?|Ckq_9^;6)W0z9~)q;C0*EUp05XFxcZ*-F&|?o;b7^ zD4^dizfn^&{>GTiIGP3--F)&55Ppd_D8`6W$$Z95UCoX#dpM^`^>1{ve8Pq zuYqII0EJrfkU|l#sIm~mmIcpl_)>?*)P{FA3hd@0kCR{4_hnMv0XP%(ND)Y6;jQ|np~en{MVTq<=UMtX_fOVvRg5~7FU&Rv zyF6I+(7cn^Ereyild6ZA?OF|GC5QNWJqd+dHhePS7`qE#3jIK~?E_QBawClQGj&i# z>?5ahzq_2fdhcLocSI}GrsBn%*-+{E=IgkDqr<#UjcHa%uFAw~1% zbHpHQ`jdLzb?kBZ(4*OBj23I70m2TZV01e`Td}k?J+W!K)81bgU6=l&bo?W|T$|iK z(YhEXcYZzTFl_VH@wY4Pj8>?6lTx(F2Xgkfl?;=8aM@8m($ORlV|>YDaU>cVOzu5s z#IFZP#a*843DEw#u;hE`;)XzYzLQ4mNe(ZE9IwMx5~dnZ^n@+if^0!^)qP>Ce3_Y< zt$8|z{CX=R6=?1hy{z2$iyHXxnA{V0T!v9GQEoARaGCLcO_DaGW)z@h7dEagi?q+C z%&9N!>$68)V)gutsfD<>6l7#&SuB^CAVgMEhQ3Bb;;p1jG-&|FTG}xTJ80K8yt3mA z-$AAJ=GNzwSsbOaSr5~$WqZVEQTD)Q-$tQz1bxs z$WK&zk!xXmGeC)UM6ZOhB4tjJS?kY_?48t9gB6kUnBDDbyQLztEbc#!YE_p%yN)F> z?k_EaJD7<4wP%@>Tb)~7w%;P%M)x^ZJfJOJj(eMOQ#s1NoNLH`x?tjp;VeOv%u+DI zs&)J=K%}1mpLsG#GL|JOG2VoyIBjB)<$AQ5pTDkLcd@0zDAJEOI9jIr)XAD1X=$0o zCYLOw;b;p!v>XZ!Z&4euTz&PNPhzT#j;J2L7!(zmeke)aHt5TT zZ*8(luU81+tdGeSHB0;>96AIE6BOKGYFnXtRcwp9oEYUe=B!gg4fHN^HiQ@`K8D_1 zdIQtzWOZL&=brui({gvsoyw+9Va-)?$G@K}o7%1w zNq0iVv-99MK`Z-`MQ5V?V_F<@s~v&ALY{-g;IV4nwoBmq_n3a3?AI4oBh4Bde$*%iUUMm-?Lr zChq{cQD&r)(mX#*tyO{yTU|CB?~&D8k~k}fxu>bjOmVKT!iNZxNER5r6@Y9^O*3$7 zcc{L~`1ny`sLm`Uyd3KK+Z2FvEO?yH>AED!cIT|{;EC0)HqT&;F3(37i)xzN+QCfv zg6VI*;tv@Tp^{-wnv>QWVJ?pYaiOH}+8N1T)n(L&PZq(fn;ZITLR@6L1-Wlz*R`ay z{{BJUS7NT?^fc@=s=H(qlJI9uYlRIbv}mY zJe5DZ>N`-tdY+XVcXgY=d_iKZtjs0!w1n60Q25q|GX+DnUk_*vZ3gTZC|yc;yIiZP zmG1QPb1W{=&;VmAkp{^Sbiq73^+}t4C(3>ilx3G|{TqqTc{eSr!D-Aj`SyBfIhu2>D)wDFm<{QZvhk7<@BV;FDLf<@6P z(yoTZVF6fCzw^rBH z43AqR56(O$Kq(8!_A)+i1^>l=^c9$cP;`-8(NVd;SS>B!vAb2B=f|FnND`zL}E4I z=%x3}H^Z62itOPKA(L9g-CWZG%MKQaLU7N!5YU^Yp`zh3Yl8(l$xerC>N}nJarCQN zKv4G3xtYwF8x36EPX3voG*7~jSm}ow6+|J_k*WTvo0{mm+ ztQNKllU7xzedCwn++Pno-v=^M2UaJP=_%4wyIAubA@uR`YHyp0p#ou2|x-`^Sgy#hu?6T^2sdELL$`HaEtMl%O8Rt@o8=wx`HH z#^lFcCzI;&hpoO|^h{(>q*%Va;()k*E+fg@#27}0`cdlFH7l@K=#z(Q%1&)QK#R9pGSW-hYpCxJx2{VBE zt%BZGSQao#98`yJ#?ojt|Aoo!>;Ld&TpsNpuh(}lIiymT?i};qR5{Q%BGp^Tl+Nqq zpE5kid~-s~F7X8qUtkd3v(snt__Oy9i^1_mn>oe8sj}b+O z!Zsx^m@LDjP`z^4kHSODY?<@aqgw9UDg+#U<+)_JA=_+6f_N0bz=k*i0<6q?vyw_( z7UA7E^isnOQJw19Qb7H<21e|EU~Vv%T%d_NAU4geC(*WtFIhBiM~1MY<>p!MC;3kXnpkuc6(W>AA18>L2zhg5>!*`Z^^qJuLqMkxS z&-B|Zg#a&bUhzqw-;kTKbP)X2@kMq16OaLH6}7Z@fVENnGbfWP9vj0UwEbq9^DT9C z-qS~BNkIqX&}Q+JJ62}Bx9qB2QZ(k2Mppe*PlkBduLUQNIM-9_s+^1pKs~)8lq+^} zeR{ICW4a;?mVHG`9y1X)MRyVVUx=WGeB^oQJ%`b<=-8ap@-u!-4T36S%;d*dZ~Fm1 z4&F%zU5yIEp4I3tl}kEjl^44zn?uffn%RPHJmQcaGdt?M7Q|ysU-YxWu75&K zzh(uN=VKDH5pM%e7$a}t^oJ}-BJ?pEs?VRl&!i2=j-1(ZBv4Zc+?ogI3-d0IJe7Y) zqu%anW%WAb+C+DHi!roER}DX7mC_vS*_R2VBGpHaqA0nUmf#5-PH6oms_x)@fe8i6 zCR2HTjnmZ-V-cI;x#kU=kc`PZ)$lGv>eTOZ;*(tf2J#ei`P8n%Q$%oxd!tERN`Ik` z%8!=r(J(NUxAE)Hc~?t7WWxxvd*t-y?mcqWRUqqMB;F{{5kvFBQ)kfJD7^+p4qaP{>F$oA1-NGFg)iCW%Q zwB@AMTqr{}yWmWWWmhV?S~9vrzXny~Sl|UqDa*E7j3E+sntdPIu2P~Y+;=zUONExT z-tU5zwAHQ>y6ObPheMB>Cd8s!E%H}eezsT7Q~@**O{bP0q-RKr{mbTbDDoprZ8%p? z$Oxj1f-LroWBvK})7tJqv|D<#5hv&Xpj!E~95P_q1_0b(g(^EPu5A*nIK24|*`?_` zWd&We_T`kz8Z$3|sq@IQMr%N*kK#xRW$RgsHYga<^lAu=6UzPd$){L(%FnQEjuZI0j zK3Wfv(kIBRpC(TnR-xBw?O8J1^)NU&CH<4l} zEmu^%{r_J83Jk;iDtVdCdBO>A8MNL^;xKJRpewWyOl${rM861cCoak73dl+AM zUUqsShaTu`!~xG;8eN*7f36gQTbTdIDmaEZ!Kuc&UtVJ-6%*~FYWB0s=IvELYKV!D z&an#0L%l8z%81q3?dto{S zK^(H}TeXDM)gkBN;tnSAzD}Mt#)yk&F_9Y_7Gz>MASYl@HY}I0T$gOocLdAilg7l_ zP+QETN}DuUyyYFa1JAQSt`aJ{m;?9iOknIk6FbhRE%QE zk({r3vvS}1nE=}L!ba${b1!SkGCX(kG(&R5x5Lz}8yMcyxRMW!AG(#^8CN7WP0Thws;N0c6PEbV(ptnV zW#8E9)3Crw>-fEa&jyZv&n_6h)|H`~W8vqQ!(deaz7cijb9kf@Rj7)Aj1gYxcSahP14q0A`y% z1i7cb5wrt287W?&czX-(ywdml5KaJ@)rh3_Rto;?!Szia2nUH!nWd*NMh`bYJB#x+ zNPjIWW-mA@q}I_K=rIr2htOqv&8%8883wP>Ig!&rhPYUJ7i*;#W3iIXF>^K2fJ3V+ z(Rpt}12nF$NJ~~pk;4yN&$a*#ck{k#A;`oehhH!1%*df;l_%R~!HPCdw(1p7qJ3gD`g+J={e5TgQNT3Wn`H)c)S$w3eWEyGjxop#){S zJ)=wEH9F;HT{iG)ZZJ1v7|efAQx`5&XsE_am^A4)#)v0Y+?|>d2JOE4XO3CG4;Sgn zo&EX^KxhAo7w5ZLcXtE$689}>`j5S! z^WxA5Vin~zWG^3u=s@oSM@>~UdwaGD=ASn@x|IQkWK~>_oj_nDCI1VXbhu%(ZOd@E zjU7)fm2^>t9+>jw+))W?9nz3Sk%Uh;wnDH$?tW){T&#KsX!-&oP;L{f$|#qvQBSpz zJjG@=`f6WZ!*_UvfCAbi{4V8Uk);KduxLAn6MYiO!F&C>4vkXY7Vb z4K48|FX@QGHbSSavd|EW2?R_6?EC9l;(&ImI=X_TwT?Z#9f5O}1x@#=fy3dhHgPob zedO=OFVtH1w|Ok%rb7UP#*SLf-S0V&o61ez`NvIt+s+q#ON;yatJ3{qGU`#C5cbt) z6G>KZvCmg8aKT%(jjWQGJEF0FSz#N}Ptvie-z*I-zhSLV}bsaPK?1;{&&8Scl&-sjP;!!-+KbjcDWp8GgfjY0SI zQh1)`X>zM4d)h_K_F3tePA9p`@kuQxfB_R?Nw^GT4plb6b6htv^VX*&Sp}>*zybY^@gN{pgC=B{m+!?c@Fc@f}z_ z4Q81;b1KHnLUtNp7y7O)iE-+*>`V6?QeE8?jCFy+{Z$YbE71jJTz=2H$uCA8(*Jen z5}wE(ffHPg5U4zwR;!BzBaI_;T3_G+{|a5Kz;1y!HuSRe%`5+S6lx!tk|LfD)yR-0 z1fm(w*t6K^S6K!9b)?bv^%<`%@?7)r{uWBQ?~1}4sn+%L>$LIOY!vZB#%q(*_njcz zw>wtMeJaT^&Ykt|<2%dap#D?*RQ|DI`8vfyZBE!{1C6w_ZU)d9VPRoPvv++OI$Ws3 zQ0=N-=UP5W?xi7} z`yzO`qi$`4lcyrr=V8*0vW&zUn+Q_VS>uL2T{Kaw0N<%q*b$8vw@SNJ_G|GhiCZJnq*Qak!a( zt>M^nT5F`q?)ltWb|g4lBi&A9TXMCQlnkjj_D+k{7413c8oHAn7*El_7ggHmz3sJV zl6JM_!OKbaNObW131f5yn}e5?JPM0#abLjhV-~9Z1+h9}Ci99kWc^FQrHL3B0@{3s zyqNab#(eCcAY~N*FJ~dACgU^-cCE9(oYm1y4qK$G`aNs4#}o>Ac)epmVfxFZ@c|Tv*Um!g4QHjCeO5Zr%1@xkmlT_@ zFWb4Z=^-h(_VPP6(&D8j3x_!Q0x6y-2*duN9+F+wjki@SeR_SF-c74lfq%P<96nJ3 z|6D00n#)|iwZ5>XIN>gFGLmKX=P9xV%85)0I)waHW-^doPNI(*9`1f+r>6p_NQKjK zUVW(_I4Ge-s|?j`bfBeYZ@oKM{LrUfyU3_p;_KorX5`ej7S8&Qt1MDi)o862CTjpQ z(e~((9x*HyjQ9q?oG18u#1)ZFYRnXas?}MsL`mn@@!hDnz5Ir6hZ#Wk5alA~F6j}T z0~WXpTuFs&+7!rmVuryY)#|sE5yFAL7U1P=8wkm#VbreS(^HK^Kt{htU(ycy&Lk>5 z6(LV(Q!HO8PG;=6YyCl}&SR`(FnwDucYer|cR`&tfXQNM2pzfVs-q)NzVy+hczFQo znRtp-F2hpH2z%fO$jy1v)+W*JKd?^PlFuu0MvDJfb$Nx5Fo~9K#b%9FIs|=#Ww!Ij z?$Gw?Un%d+!V36C^ToZ(o?@`r+px0rU9ea$~?aq6r z9F@#60nfupZdO=YEVl~UMCOuX=_>giP&@2O+GHZ;#pAv&*&yp2M#@>~zc8)F-lTgI zUvXsG5!9~i{F$9I#c5HmEI)$IY=p#mVf&_io5ifBzC7l&}905pH;jbcOx? zN4=g#XX>IgZg_KrSxbx(VC37DdMp5yQPwTk(;Y6ei!dAe4JmI}JjsbvW|EiRK4y^| zz%Mo!+RI*-KiZJbHEM-j%Lt9vbet^r?sICAAAWt@mg|)yCF%s}w1)XO^-%fJa|(SfvWRT@XPtFyBKm^P}H$Mdd%YMmc1=g^y&$yk<<`1uk6v@*rkhi z!6tin>a>;~BLQ4t%EUVrEZjIPX6?wB!`s{=1g|j70vsG@-QjE*dF@2p4!kiH@*_pL zox93*CpC6~SvDz?j@{r{((pivcgVc7G`=oq9v-9En{|FBJ@hD2Cv`VQVB$7lk#5zr zOmQT57E?q&I8U%nJPGWL#QcqW8n#bP{Sh&Bw7c?*PHy<-6uoiMeFuT83>Lg8*NBP?$>nqt@ii zl1o_G2by>b3e~IcMNBupOj>eSTK>sG zxBcS}rkVbX-suA=@zE~4Tav6NUEA6iE!6Cc-h=lC25_F<;^Hpb`LzjoAw&HaXS}?; zY;nx;l%0Cpjz^EJ4Y!ZqV}y~6U2%pzq`kEo`%+oKoLRK%g&7RgbGE_vHql6e8z>|{ z63Q=#J!#jM6LL?h5vCIV+!Bi~goVmxflC^7r-x?V+}{%Ns3vvC#7)~Wi2eo8yCi#$-2uDQaq+O z$`dKFlDp}*>H6|?u<9h&FYgl`%WowBd2xkK-rk$Db)Ay+bJS#%AQk0!T16xsO68ZG z#y^o6N1c0lNk~t2;GqFn2gmPh@r0JY=)^o1on zYh{1Aa>+9aBHsv`&g-jx39}xnk8!Ci33*%5E~j;T=_tyi9H93#}G z{N?);BjMZ2p^rXHX(rzrNV`f8{aO07IWa=V0VLT%ecz@7j0q9Rbzr;gxO%T*07AIK zznCcvKa$wp&YbIcF|XCjhZMHjE{vSBW52B3E!4fxwe~Htzwb%=Q!r-<;@Uml%1T+L z6!X&(1qCHz^|86vLpZT%_MLfX5VdGpY($>?3(Omgtpn?3>l^9t9a&#HDv2?88HZ5p6@c-B{o5?cBRj zGp??`>1c4XCwi90U(}X=y3gQ1u#rGw)7(@~$Pk<(9I>P9>y1UEEC*36#XG|Rp7<9{ z0y?1l5#}YOVkuX_P4JN&BRKP#LU;K_W*e3mgeP-#G1uxwn^WIV#UeSb=hsW#hIt$3 zkBo0WCM(u>cM(zLqEmUVcy zd=$oDYRonz4|^p33T-Agy7*=8Ee?zebi{FYW@gbmYDvm;6izgkBZ19v@&e7$~epxi43HCbRaK)zG<#gw#|AOMBOqdK-&2y}9JDzFVTN!tH0~5Qvp5_dG!KObkT9eb5rd)2+=9d(T$oOP4nWwAoWv^B6K<6C%RqW-kH(MwaI>yuqHa(RENWd%Qc zaC}0+zcd_%8Jiv<-7U_F@vrNtzRuGN0|a|C%@jpeUhyGBFG`Lp!Q6f&CF1Qzb3=!h zs*OGm`l)Y7-c{+*D%9d=ev%T2;(UH9R=!WU`kZx`85tfhv#2ITFII4M5g zOE>yJ@|3jTfoZ6=rpRIW8fUT@`7!K1-rS=DT?mv0N_9``qh9kF8ta1~6|Hh3FEva? z#-Om?=VMUZ-*uncr>PiS+}E<_?Tn^YhV$K8-X4;BzfupZ$_^`ekC&?0RyrP+sIqhJ z|M3dQRCv~##y zq1Yw+-E|SXJVU}wFJ=Re?9NKmR(bXgo)Ue-)mPdX>^i|@FF7hIDeLU`7|glsZ*RqZ zoV~KL-?s%ArsfZQW&EAZrX#lw!@R{L3pMGo=M9W5Pqz_IRI%-lh*cLR!AD-tAn?cO z20H;aK~oj(iyqd-r$PoCJSpCje|jy!*g4?eMH*`7Q1h&g|PFLv_q7^e^gDvL$c9K9HpM-5 z3z1y8VQQD&O8F=rEb4wbKGb0=A7Dd>7!0ga56CduX0^CN zug^S;mbAQd+Z-xe^q&m=^hUd%F{dkDE}r>FqDsD9jV1cpNV+wCNq9LEdddA- zYwZy@k_7sIgS@xD4+D0BWZ7m^iOtCPp#iP+KuvX~veizI=W0=*KnGkWma}gQ*Rq?4 z>F<6J9&R|52}@zYc&!dJrg16`V{Z&xau`eltK=5EzHv5E5Lp=qZC%J<;K5kB{5GG5 zqRYGAnrIcs-%fnCWdv{;B`Ig_hQBj-`>h=f%l_`p=$b0t+Wsxi?Nm>0f>E2u)Lvw@S2M(maU|FJ;{H1_#E7O zuKa@u=T?G7%m@ae{;q%?llM~72{HiwI^*{8BD?42uE~geJer3`lDk8!cZ!5_+x@22 z@B${94$eGt6mwT%Io=?7SWUM5_>uMoNqQa-`3Z5C`SkerwtC*N$D;36YGxhj<{3SM z_|0+*oIl%k@ZqK&%cq~-;{VZB@N&w1bMv4?K0lwP>6St;2h+swubBUPAZEw*&6|mZ zYe&&eN5n+BgPN`GIpOIzft!OoHO$n(pD&m9ZEod~0Ykdy5OqfedBZP8xJ9}ARP=07 zfU)`3D&Oo%>xTznaLIY1yyCOa8dm+h>}>NvOXGWID7-PTH^=gs^|KAJUz{d1RLl>w z5;~^C6G~6eb$os~+Kqld=aCnEC?ke*Ad1swew`J~P>f7{SzAg@_)$6m$MXI4W=+CQ zXmQk7K0t@3#8dh6Ru;9r9bz?HFnxqyhOWu7NT!a(lWAQfYQ5&eyJ&B=*IVoL1q?JZ ze-P(f+up@5@5*|D2(pU$sv}E{(E^RH<~y(0GJj7T#^)R1zj(m>50;T&_em#X_v7%l zS!XNQ&MZ&-V62_}Erx_b`0ffmG!&P=wsCwkK&r`^78uswzi zsU{gPS~!>e)57FONUi#Phj~bXV5%_PSH1c$$arLTXLALegi3OI+R2>}M||DH_jG<_ zDPe!q&85K%c(k*j6>jnUF859RS(gW6^u*5eY6)|5iQ4o;mCXS5)OI-L%cv%Gd4Z#| zsM!>aXw4^8IJ2euE!neX`T3C6qQ@<^u>&FkOPk}Ox%qoh?nq>y^C7^nrd%@qeodae zx!-GWy%jiLej`yX_9en})cuBB!Mz>XY`I-)<6DT94CMdE-dhGlwRZi(f*?w%pdykU zLAs?=5s>a4N?^#LLmEUu0ci&5?vfa~6s3ml?rs@|j(2m8eRKZL{XCyvKCx$J*SglV zdi|E$@Nq3o3hw96oKhsuH(G&{Kb3+$>$)9ASwe*n<}R`Q>e>Y{#V=YrBWXNTrFcH`M2?i zO;~O5CxuOWA-KObo(pxL@-*k2Ank4PbsbZm1Scq6-Ht#QG3Ez2F1*+k6z~HS)CP8l z`*jX{9S`J5x_{P5{Ya6XbLwXiP%J%#GCDr#a7ZFlvbKDu&_Wc*)J|U4Dt+;Zv|@U` z6z`VGW23nAZFd3I{WBge{)LWDr$Mk_;Z<1)o0%{5ZSS=*3{Q3%de(P+Hr9MJ^XH}Y z?Xjo{{%NEKAoD)upf`?S5R1m~#U0)1Bjvl3Vs@ywM)ctDm}4hz_>7}9d8VU$XSzb$ zes#@pQK5$({`KDa?Zw@qQsj(gUskD+?2h=T&OX!G(X++<;w*$8B6`EPilXs0iDM(4 zm0E4Jti}uDG=Ih7D*Z0dcf>oJbBjBOo8#lJd;XWo6f0%&R0k27&DFz#`8A_yN#2Bz zb=HjJ+%VTi8}W5-A*~1DVWQ)?lhO%(e39!sc?Gy-;UX4+ku+rmp@xdPl5HDLE3Bqx z=zX9Pq@mrCYI`2luhTf;8KlAc4~CmevXn5{&OP-PE=#Qcn6o0Z!7n%{_m52I`KO?N9bY74K_JodLw5a>F?({d%rD}zGRn3Ph)JmJ7GU?A&xDi!qS)hI$RN6%kdHf~;ZY-`f zH%tAjiJ?ROf_NP3epQV5oTGsjF;0dsbta22d%%i#uSI)_rMDRMhUVV&$n=_`mn=xu zZf6riS=?~?rLcyWMh&?K9sj7~#1bru3?ra{ge!)f5_V4xUfCZA-L+mm#n?a8mMb1M z1iwogdmT(0!6Tr8c140OIb2{FOSg_dQS_KY*Me7M_~##73X&OdSnXZTL*^4QfH;@ z5#t3qIuj4Ysfw7aA(O!;0pS_?o$wOh!BH}Y$^vh6-tpM8Q!WmbaD998nC}Rg;nnqL z?%IP-I;V^#!!{Vq2)L%UVgwxr;*Oi3`Cr9p9=DtMk{%fED8rSvv^U5c)|j+k9r>4$ zWJBZQ_1ukdaWJ`t$?3PM=^~9sKZrivdCLY4o<6K$;W389&@J9kCh^coz*2QIEpce{ z8y{Tau=jX!SvHO)dsi}>#4*ThGei;U*-#5v6$$Sfs$l#^J2mw8(_Jf%aWC3oUJ4Hq zy@%EmBcgAw6*F$~#xD-#cLx{L1)U=t5uVB$od*e&nkipwA4l0~ZcQ_t4bvtBsXaJS z{X*CtKE#pfj9zg(4Ti3s9kd8=;~UwRC^g^5FJ3Kq3XyI`TARxb?6oduMO6&2k4OZF zcTD>o&@r|aE^M~;eTV%~MF$t~%k}nG?jlRccy8`5?68*&M~ZW|RM(n?p)znE&6LrN z1l|?g3yJU$bDVVI%5Tr@cPX&g>mmpei<83q0D3?c1#djHLc}65=-HSnoez0?2X{Xd zj3zpNFdTStq#t;6GNS3-v&)5+$34K5e~cwuRe<>Vl_le^N$dTmIWv_|-ckF(a=gwA zXb^YAEsMa*F`4e)u>hp`>o%7LqX}`PZ#euTmZ;T%W?adEgo|-c0jo7}Wnj~@^Yd+U zyl4tv#7omT(sH!|;momp8(u=TMb-*L7~{FknKj6wWG9qde2XB2LNI9jFPBI%M~9(2 zLlTS32SCarda$pUq{G9pEQ(2z7Z6cJc9VCp&Q}xwLFae5|^`AEOl@tb9|kx9KQum>C#~l+!n(+|o5ds#>fykyb@oKl-hE&{HZC#|Oc%S? z!8n?fcn`&9+!hk6TpW(>t6DOL6-JX?cqjdO?Dw9F3am`^v6X-`!;^LV1#>Bg7L&Q7 zuP8gNBjl!gs`_|WFY;411)0Ar=8*H$i~bt1*&N1E(tc;7xwiv>3~Hmq5>(e31z9-``~cCz&OHOZKhv?B;kG8G&t$dJVPb&@tp;|+)??nr1 z?w?wi3{_+UC3|8R8VeDDWX&21!%T_QkJc0z3Kad0U&sf*nt4UMzp0R5X>xx4UXRg_ zv+Ky&7|MmuFNRmR@9ZBQrn>JA!Fquj;tXC=TS_vu`{Ye^iCc(v4ZNXv0m+QL|`gghTz zO|6X!;|GB1t;88yDEDGIjV3qRIyyRB_c%5w&f55FT*B;`JZi>nS!J$tf+`A><Toj)*h|2gyueW1%vG~M04Xv@w8apL5zJcymX zx609(P)^-cro(JWqk5Sbhd|xXwIs2L=x`uh#`_btQL@8l!fla=YaWS}uQ+J+Yu#)> z+4O`uOZb!TH+6zB@*fSn{9&HQN+-Ja+Z?$&A>%u%d7LsN zoxPH)Si{|krrfhS&z2*GzlHCFLe3FRDb~hrU8E#Yny&U0MYC@vFkP4MDJ?0iG3lJl zm@g^CE{d;q9Z!=`L~QO!eeoS^Y~3;9h$-{VSv!k!$Rm1*OqV`F$aj3Ldcx_*xxA~P zZ@p*7wkNAjtYL=OTVLS$^0hvqQG+<)y4&B%&`QfIs$g=j7vB-KpQi_#;un~RckmF; zzDI1e=ATg|HL}+~JGom~II(<;8Av$f8>&$5+SP?pgj;A#@0xLbMi#9}NNoYb~%-=SSc-o$>Az@JCoB)HVEuwZ1O z-tm2I=B+rPjR6&!N_QKhf28{_HvHcr3b@1%n#mNQ>kLMKn6Ty4P9+2YtX~-eKAd(^3|80yoyd znTaq-V&x@yC%F=#xu*|%#8mne%Nb0 zJ1=s0?vn*2l%+Yfy_Q7D`$QX0JY3fxBkO40Oi-j5qas)UAwml1NQv9-ZE{;cSJ* zoM~#Fp6-m;)WZb?$(OSTdrB>FtZl0}yc6be7-Op`5ECObZ;kTb`=5BbNx{iuu<}V? z^J6N|m~dr1Vozga|nC>e8xS-s!B?T}@% zTwB=`ypeZjxoPctZr2Gd;LLB4?XY_%JNZ*aVA8TUhfZ#2sm|pb^i%{CoIalO47Tvtr@pZd zzHH+&W(BNa<@zy_7? zVoMir8K$;;QL8DX7lnn|E%rxOPSP*Q_e((_>YHVxwbrr%(Q^vlHa|7_IdbsT0r_@; zvtwiRGJ#KhpeEcrEdC+6aju6CRR(fvI2vAfUx5*`SEE`Q0l;Uh2F=vNl@QCoy~;&W zgySIbYD7sGfRE{86w|xkkr33mY**VE$4iib{!%-HR{6>HxQhIv;8<66D7`urovH;i zPjfH&rRMAmo>ZOrHLRS6K-W4m1Tyld`z41!iNuN*-8)#S zfNA>zds7P;C zhTCZ+08|}LA0n1)@cPdF{)*Xbsxxxb;PabKAd$by6lUXcR%Cd2{$P=eMTbN0lWaPn za63BjuW2A4rgAGwNSz_L;4E^w2hd3niBGv&Z+Rok*@l=iC1?&hRi|wl8rW@*FLU4S z8$!f^6=3QE>Q|Jlk~E?Z%nd?K5-5kdMzx&ZS=`rKr!!a|>JL3EQ|d#-yj;w$(?w5R$!rsPq-%sK`_u#n`{7ur!21|7B^N+i0@-Fw$fI5rn0zEPF6^ey5 zPDyGCg39dHhC~5aXL=MAcb_|-8e;Bl=Z9Xy#tu$!Cx+OT&o;X0Onr#1Y-IBQq%YdS9jvdHz-?Tq;R;JX_ zR5&JqJp2TJg*EGVNZ%n_=WmvEo_;N6P(1B=Cs6^Zt(?IH)$)Yze$R8cByI4fm}YNZ4wt`g5 zA&LwKu5o`@kqU(0P`!$XOm|d2@~1%idC2>>q%5Qu1RMUk#$-j55hL_dzXaV7V1R&MikTdNPL0242(S>{{@myeduBxe~6kt1Zd@ zUEaB(9Gy}YZXL~UJ08!4ZO#vUHS;L>xaL!F^z8T;fjXSk9-EP2s!Z47cY9JYPeU2` z*KV|DK|#>Ys$-e7KTu+;G=<_T78qONv^k@Y3H9t?baH<29xA5-CnDpq1Ife(u%Ojv zuP$_GKJ4T}#Je7?R}Hg=r5%TIc3m2miWq0la(1X;s>7bD*VzR9Xz=(pk;*o8G zXT{IM_)d0sw~>RJ@8R1vi7%COT))aYDaNqNr%i&-Aklg9=m-EQ_czzT94owFgQ_5J zR{i!69Hz^C!f zyNVSC;CG?;3Y6M0+$_^;hP`b*CN#@4Iul}=+QVX~!mhUb`iYi=Oc0+&jr8^xVxtwj z+Q#Ijwo2I($>z235%z+#KImMz{6=Is$f1@WIriae`!Afq6T=V9E8J_PXf_Q_%E@;N zb}U?kMg{2Kl$!Lippu=G^*X?~)7igIa`1WyfPa5Te2wUGuja5F!XT4k(HT~c8UEmQ zbZ3DNV~}8|(mp7uC#L|HVyKJ!Njf=N<=BMwDI&ooRlV35z8S6YRp84>c4(vzA{zdVfKu&r7h5NqCDcWHOR{e#v1^-v-mFm+}2 zK>#k3-bMr<-Z+TVCnCpKIOm>YgoMcZ-1PNPVV-LtpStQrWNCiiW%u}gG1!fE_X;fA z8NhtR_Zg+qs-_q%)e_X*43l*ahN~*H4PG&O{CczQteV@>O|HU5B#NWG{E4TAsq5+S z?Ez)mxH8;V@sE}VL1iD&zX`3Mk>`K;?~5b=gG}Y3oug_Q0-yuLxb&xp839t4d6D~r z?9`{%Xu=}0ve$*|}l^%dyQ4NBK+ee$FqG#@KxlZ>+e2y~3#hy?N36riEc zR~sltGIC0cZl(LI=8m$&may55Le?otdSH|l>9S{vp`nZ;PVD{vO13|~@JsPD0t#Tn zH;R$;dM8JdTDlGVuKVR}@4rUof-2Mj$r94n_b5w){qrNmQT?FtgbbnVSe}^oFXVx? zi#&Qzxx~4d%)+ur!N4F*t3uBNfPP0= zj3+KLA7-$&e!C!3V$wU5U$1CrPFQ~DhMb0y97z7*?N_A$s9%hhoCDMZu`oU}AAtx; z1Msk(Ay*<8nBYijQ`79_C}6CfI3FLT;9Yj|df9B{JPN90nV6$mlU_zdVV?KNQP8uw zo3m|kfR#V4(ob*bcH|KDE+!xSQMRL;o62~}d?F$`TywH^+;EI)GA8<&RAp*=`hEuA z!JM}fBy{x;y8i27EQoxunUreNReW_KO|oUAC)dBNVS9HwG+)b-b&__%o-CitADjoyTEj z>Egg-o*na2m)qeEE|Fdf2nN_xb)KdN+oJyrIW4W|M$M*rl?A_Z+u&dx=Rr=HVOJtX zzGkP&>zy4tLY5h}bz6RAg_1EC-H#}-eS7*p>row?n3<$+Y{V-a!LDqw# z2q=_+NaIe+#<(v4E=1Mx`K_hIaFN%7E$8G|t7|T9M=U?n&6`K5B3QFX9|;G8jjBx! z+xn#J(}wGH_*TZgM9TlV4!?cjHA7n;+A6a%H<0HaOh=QoK+0~Zt>fzE*5)Ke3@X3D zM8b0{_R#%65~?e_y4xhXFMhZ~2-b?tsg4#YbV3}>OEWaXRX!xy>t`f)wnEDIAEt;( zhX({;ul9;Ws}^t89FCL&D2EXHt%Tb#smeJM+wGhpMEgcLmia0i4r`9vrx=P-AAL;M z2w=KHs@f-N=`wMOOSgl8PdJ~seQw7Ze z^iVSDdxl<8|FlsCxUFsO;gnuC2D{|!7k_ncKil4i-$9`MP#*tz7^@24p~Ni>=XRm? z(klY>jSX@mZr^#Aj((61D;DMC-!V5diOnW;Eiy}~#7(~UHbUwo3ww@`Giehb5 z0(A)4OM zZeB-{N1R!WkBqyne*O9t`V#bT)K!>@NA7X)Fn>mKGj&B`J8@P9b2}?FwNU5Cm};h6 z2e^X&H2`)x4rW(AT}t7H#M8Zb!VX^o6cLVej^ke$c3V>{4a=4G?<75QC>zVn$Mv(> zEGJkY3ym4=$eNlOk@n0N^~vupV+5>6(Jjs-mT(BCuIxELN}opb@n)cASk~3JF=`~y zehb9>;Zyv_p^)`WV3NSwc%=c)1~BeAfILly4d2Ob+^j@^0q9lyuf0$sn3QeFyywxb zTg#)_nGyR@2H9b^KIn!jWrZTJZ5CPwxwraXULkYNjKL<{4!)B) z%mow*AI`5h)uu9JRXf2g6hkzsotL`y(lpC(74kvB+IgQh%?5G-Ye2^(A9~q+%8S5Y zz|y~6M86R`OuJ@-d6C2^IGZ8K{2=Os`6$nR=Qijuan7D3!*VVa=X#=Q0i*N4!UT^{ z*CC?%#a9i4{b=cCFY0T3N(zc0kP4t~h3k{-FQrsGXS}YISVU@-$*7#*f{JVV_SwZl zSby8X=h2D8_gC)@&eA`OV|+VaZ9^#nkVNn92o5I~$COpu`vRQxy`>59g^m+?$qz}{ zeZ`CGtd1%xWOPn@K)K!U6-1hLe0-7lupPM;Sp!KuuPCR?UH!VFds-_bB+PO-eudWOv5#yoP^zts`!AVD|B~DMuZd z$8m$Y&z!=^Ru6bPR|OW@&N7&<)em-hd@DN*nCo*i(N%)kRg6piNU4hz*zjz(Ui)Zc zJyfE~jRZ}=;N@tx#>VIkNB}IKa}E(*c*+g4nO~^p_+tOx8Xz*~<4MB!5`iN67%+Zc zRSJ|F7b_><2vMVx(XLP~p-HPQYbpxs6!Z}8Q^*-k1N0CSK;CZKuq&OXeX;$8K=4s4 zfP2^^jq<|h2~m_$yp!5Jj!cL^vFD%elDAC2^s zu2cdFf5?R!Nld@BR(xV!`d+J|z4d_pc*cL($FRM>En%~k_feBdZ*MP-AeoZks=9be za(??#RtFh3UYf(g+@@vhmz|j}28fDd;z!1EaI1rXJQo}%*8ld!DaFn$FDIz!fg(H= z+-qwP%{)ydYzyE-dIJL*hULI?h#~#GQQD3|^PW^fJd>MiN@X(kPP6Z*Y^y~Dm#3$b z(2@N4M$$)<9y%7y1Z7fy(!QcN6EdYgD@)eQSiX?)?&B2t;rny0ZW4%=@XE} z?mPo{OKFhO1fHT(Irm<-acsxC^1ppg^mEM8k3^&PzQsmNcUQQ9X(H%6>ggz#*RVb) zz)=TF9x!(otsfv%)xh($M;37Fha$p;484{1`oi7Q4NW))Gtey60-q!}m25oAT+6L^ zPfTMH?rlPGsWA$wK&FWDa-8i?SeG%=E?bY-i~wvDF$Y&|+rzT#eF{v`0e2WV>e6v_ zH8tI}GS;FJ5a{lT4GOY$vRgg$X{~4;I#f42LC_Pl%l{ls_@A2R*PUGd7M(Ay+>)`M zeRa6S9R2GY#@zl_;|2gHX^?ZS7+!7yugR_y3yN#a_eBHdeJG?~%K%x!R!k4S??4S>bhRb|f4$Pk0pf(?$3+?S|wFW*4gxLImV zs>C>tkFE80(!!or8Cv#rvblie+S}bS|A=Y)i&JowTyE#QMErz}8CmZv$}Dz;I6JBX z50B}d=si4ziQrBed_26^UXupy+e0t9O}YCKi?j1Vf>3|Vw%)KWNfl$;uERx!bFmI_ z5`e3d&O^K3t={t%zZ-Bt9Y@MfS52vv&8QihrSbs$JAE zCJz{1C7m5lvlJ*lgP^@$2|8Ky8BCgO;h8cjnQ{YiO*)#4BdqF!Oj;v@+jj9m%3EMv zUC+r4PE$RjRWeP7MbAdxpGOd)9&1cv?gqY z!!b%$N%r@)mR1KuDq3?WjZ;!OCFWai+uCbARuXVO4hK*V2v{K_2@8Z+2%7}J83B-d zP*t%D0Qh|T0{}kRwv!)rsfLhEprgpHyyE3MZ-q41;mktj#5g>rFhDx>TmLwX3qBhcKymIL6!Ph_;FF znBf~F!VF6j^Z9= zezSL%*e+PLdUhZbU!Z<#ImF&G(B4YUoyUkjEGk-yiIawsvVUVGKE5$195BKjS%8{0 zR*Q3%R#(Huv!2kXqP)C(1)CEHEJdwsR4m>hWKAtN_tsjnf&>H@C@7K9DaZh;*Q{49 z6f#jKJp=*l&*cXV{7nI*4}%0odgZ{DqUbj^hFyk0Cvz=#5rsU)-N|0}Co|>W-}hMQ zBNVRHfXN996cFCtH$Ie(6#+o@`|f0LeJ?`&C$1K&T|YT__Wa3{C&Q9oX_8O*JW{#o-90V9sZdMFHlx5#n~9y?@4Q9ReXl3j@9;Y5P9oXU2a=`|%j8F=C!gQ?J{`P4&5DA=&2ki{L|WWd7c2jsh5gkC^=v>J zBN@QMnU_p_{>8>;m4v%NuRr_#Og=-4Y1H%Q&l!rjNfQJh9^=5!Az{t0*TiiW0!~o; zef;C7i^1(2!j_`%oCorCh3(}H=B_osoLYv65{>!&wX`?Bupi*7)w+7~?N2cSdE0vf zy|%(f@S{z^0wY98f#%>{^1C++C%a^_6l#G9=i}1A02&sRth0|zPRLavFP-Z_u-B?TtRPZ zf5ZcS>d*YYO(0j)dmOn8rf;?Rv*sNEK0pM%sy7d1qMbT>M!k%J34aA*|t6v6*c6Ji+5+Z%K#V8OLTS8pGvPm$!rSZU0_Q& zG~mAswuqc5WoI;^Rz$JUZ*r&d#s~_(hVLEB$DvA}>+M ze(5YIIS;3F#Q?yNcef8()R=Orftg5E(ULN?^g!=+Sx&xomVOh*VUkImp7(5{o^g3F zE8&QgJ+A{X^Q1JoPxd8wmtMcyF-1zsk{XNQ*-BZ)T#(JS*J45Ok3f+LQti#@5sV_lU*#GofhqRhkUhMGteAUE8tQ@b%+`+KDz^>x3LJV&CPKKa3y7Lt{SQ$ZNKPk=A%0Sf6-OZ(0In( z4z5hpOLYg}(gTf?sh%|WVN!dIJ@ZhZeunu#F}>2`fV$JeNJIJBK(oPoaQQ??G1Usr zV5T0nX@H+!x9sg^cfduXY&3nl`Cx?28QE`rP?-=tUmzLTG(YBq$kt@t&%&55?mUew zaA07<#iV_(K3evHJ{0gv#)Hcu#0Lcm&J%Z5*)DrBKCbSp1A-a39)a^2ko@0g9r`^X zfAr{48ZhMksILaUQ#%A=TbvJ)yLB=XfAtn83i2n>%HMgN{#w=kUHTf_CZA%?5f6z_ z!&p(n*q#F_W(7uNUEQkNrPunQ`Y@w~j|Fw7?XK@?F!)_27|YbrxZ>+>Fae)s)kG4l z03haXqYVo)6%`?(TyTJI8R6!oO%KFm!j&M{1Ix}R{sU~pr@#=ZWO^G3!DD9GWZ9+# zB}&yr)Bfz|nS%O_Pvk$E1xf*eMYGgE>u^whPmxVIUjz09Y+DLWDpBtioo)$Z0q_tn z&xe1(4h}jg%W(brC1z$}0X*whO@KrEqHcNya0M6#*@%D`mhj{lOaSMuRY6i*HCPG2 z`2XyVn9p91v9vJku>EDTohKAky>31bJ2MyFquVjM4gdQzqha zXl7S<(-yzi8+|9(-&}i3A5hS7OVi_ka+QZ8S4;q0qtNK+Y$F8h`n79NF;bQ*gvj-y zP-5=T6P@L{WCq@&pm+10>&tI6L71P_o^%@$7Ncb|Lnx80PkyyqaBiK56#T*J#vTy%pPvOAvxG&-PyQ8?{k%Bu-xr>FUwYRhRi{g zXhDzH0Q%0YhAhAt4L?Mh5w>ne50^p;Qq_(FOed;qGo$YQKAc*}nqX}H1%&FS4$Ng)Wjv_T|E93IwHIgfFw5g-EM8Bz(fA+Q)>YegZ8FQ4$ zHwy(!a_IqeV{VQ{Q7PzJS$66%{N^p8$nzWx&`n0d4p=mGK-#SMDLPG7>dRCf2iK=q z1}b}o=blkjq&Pq(RV9vqLsgf$$BueCv9W~o*g5|fa;UQH2c*? zKi7Qoa}Av3yRV zq#o6gN(I`(JGtS*0BR77(n}BuL_2#j_1>>_W0QpREn4qyJ>Kqt>$kVpWWO_O3cTD? z<02+6$!LZ@5)~H2;SEMC;Iuip;Px%%n}k&s*$zHdv`2DR94wq_Sgl-YFMF3sc;hna zF}5^1I{GtU0t~j{t^r?(t~N4vXYskyyA?6*P)tAN1ay9y!2SKF6=$bO&OlbAzi!f< zC_&#%cD;D#BL>s6Nnag~LI0sb9Ul98iRj+7$>@TmoHdFxG&CbNaF1sANU>tN^Y-Ax zS0IztU3@SB;3l?pH8xz|JJ0w0w)eiM>jJZg5x^{BIVu(wmaY9#tRmeVQlQ-Kf%V?p zrlUW?HfP&x?(g(-1i$YFbpKg7)Na^m1s1tGg8fE7&^jX2=nUrMo*{%8Vy{486Vok*|22L}5Z3*}B3cFvax^aH)D8iB zb)#$H$D+jwY{~*@=-4aEf`IY{WTOTU6wNVo=bhZnY`0yBH@))(tzAvll;hJpJVT;* zfgkyo$#Ak4hraGE+kk?+15|s!XJa3xjTCPf1rm&MIqi)L&&fjWJQkxs=BK4^ ze8>S{W8Gv0&Yn9%E7M%vQlGrG(a<#wNF9COZavT@3#3AMi$IZ)sJCN5DUItkf31`( z+mKl@z4g;z{P@uj(L+3VzT96_>SuU;qd6)y3Q4Y#{9V;bO^wo9M`nz|X?^Y5wM~LP zY2&gfG@yt<1=%e|MiL8!E~ITJ)EKHSA6T%_++NbM;!HC{^ULzo5LItC&%_`y_22@~ zLRb2jj*zR7CwO#U>~izF)`6KC)%oZk3={qNI(yKx7n>;Bjpm$7lY0o-rXTS*p=&p- z`p?`KWQ*d~E{~ex3hOvBg@nQIceulARCeqzoxH7s#dp|HU|ghG79uc`5)%`A9A~G=CnsIE=C>= zukEaoRCw%-FV1!3a~hd!KLNco+5JSK!5lN68$i2nHbY?9s;}Xh9-Y_8nx|dU&uWG! zSIn?-LPmITbY{6|U=Vf0^FNL@E=G5DAxy|?zp_TZ)8F_3-2P}e7nznA8Xd72W?kTi znM}XbS7O3H3^R*A-b7NRZVHbZ%#F&%Y@0SV&x8XQ$0G3`ZrVlVqQA+WFU#}B>34Z# zxc$r*orpHa<8;^IW_BWw+kELv)mNoQXr4Ji@)8HJ(HimVCH~p&p2g=Tt$}Q#G}g9@0#;XV z7-nk>?_gmuwZQ(A767SThL_IpHbyHOUq$N<@K-EReyrF@9}cl?u+TsuD9A zE?ws&_)YPz?t==hFi*3Ratt>aTTF^Sh%X;`|VC6C^kFo7BdX zmtq;RXU{|f?*26o&k^in)E2K%)iK#@Dqw3A4{Nk9TCYBI9ZN!I$?0sG4chA{)z+5D zq>N$cRg~2_sxbUniu;dzlp8~za9+(DH-&P*9_0cv6oKOG1G_pYXIQ>nfySwwMrpm!Anb6Dq4uX3m|xi%0Uu5?~( zR{|MTvZ$E9a&D~20L~hFmCn=80+3jOxv>hauiM+<_4Svsw5qa!`tb~nG7Eaz^3Ev5 zqB~db-7M7E-4y)mXqP191%z-#j^S#dGg1!79)~p;IV%=}vX9QueoM@4fykNE-b!|R zxmqze;zu`#rm`o)HDO zg?h&=r66MhuTKQcZ8s`X8RB9?z-;v#5BT_8N5g3p7dR~^#`dp^y`EIwcrS~hs)a|J zU=9K%l;l&@@3{@SECl?Rfz5oie8oeMS0K5qUgJ3%`aP@6KA-DnPtRjdXqluvyX(bi z>cxlYd}rsL<*l^({jY+p2&MoIN4<^MPGEL`CHp_6jUaI#psot!^v*-M&0&}7JlpVN zY;_(1IiaqQ2VxGxRgRY4K#Es(af6{oIB^P{kB_hC9*23kY)tpo<^*6#5FC?K2#lmITriMxAM{(%{87mr+aK?)X_(s5=kHy*L`wedi1c3Xw16Y zNzK-%rN{kZBU$Qj7h8OQ^T>Ho*sG?Oy3ei%=kS!-Spc2;4r<}Q#sGxg-jYL69@!PP zvn$#A{1$&T4^&ZPVQy}&3j%?nQd21eAravw7p2-^_i3n{p^B02pWoqJ;=K6D=es^d8*uWCy=an_bp8R}#bF)D@P#(R0>CuUr5E~#xO0~hFC49*%l%5%| zPrvRlK@OeH@dnRJfh4xA2#GETG5+>`-^?@5h)a^RnqFL*yB)l|#*cll;_@_iFi_Ay za#Pd3E^q$z&Hv+C`84&|zWE1ZgzW$2oxfe70+{x|tO`>4-e~xrLhK^q)U%^s<+5vE z?8v+*D)0vxe>rT904Q29ll-lV_#z&<=(Bv9^ZqIUN73)VKNtPzKR*-7N4v_^cAFsL zfA^49(txL_g{WXN{qKnJoj^8}K>jJeJTu@w7Uw@7?dMNC&CWG;bZ4CWubcJf_y6`^ zFdguewqHaH|91}wQUtc>pzB7!z3+17zuF95ARvM9o{rIW9@{=VL~kSt>z5*LD+Uz* zO>b8gyF9T@|&1GQd(!F4-ATY|*j$#^ zU9;SWfjWX6;oYBiVWiMS@^>AdbiKZM_kT>ll)wM`9jE+D;Q5)7MxEM!Weq}m;F@Nj z@Ad0|YRyIK+Y?0cZ<#7gV)gF{fc`e zhZYHcan6u?Z*;mD1+${}jlMi2Hof5p1qivy23~|S+~ve3kxCL{)-Ej zzXBHRz@;q%2H)&0HRx}i{^?q#`cI+*WVIqhk&PrY`G{+mKj10`Q9PnG(r0S!S`-MrE_kf6QFrD5 z1oAb{jV6U!*WmJsireP0#w0)1gS-^{I2$k?m=FsLVZ+6d&-z;K!gu>vTTQ{j zFL4-@x&BARbN;4X)dn0G3~B_ZvjOKXxL^eJmn*8^++edqr}GohsOY^|Cp622%_vaSzt z(H!0A;UI&sQ6k~1irq#?dYA7vx`eVwP@ZgWom5(r2e~;SdSM6fZ!`v}00SCcI_<1P zcJss9bw(>qMSqn&=RHKVclNAH2a8;C2IAjSnl@7UY&5jw-H)%y zFT zK6MWj2r*f-N3*-^Onvl50;J^g!6vk+uwCpRB4E-!&M>>m{Bu78>wP#wCHfr;;DIPJ}TFL3yPuBnSMv-@3Y`0Jal~_)?3jtX#Zhj>A89Jhc;Qe0+Qq8WFU5vGvWL-=43k1Qd{Hl&emx z_kOwRML+!GGt8#|p;7hHbS&CxVEiuwP`|A3kaJL;;D-?Tm6B+fvh~GV z7GL82xWSLZ6d_mFYO(Q3`vgIkUGq0QYz}KfZFd+n*2YR9qq2?Bb3cCh+oL3A0))ZpX%VOsf08IU#N)GB-~qD}zTTe^7N(f~5a9H=ebx=;n@-~Rvv zrL#FiS4!=cduTaYX)YETUVq&JS~9MFt+?2t&u8qa^EitexT3ACtqP1sSUwq;pSy6Q z2_+f>ik@D>W8kyDIYk4 zV=rWsL~1lX|C#i$?=7N#-ct@0h`nnAD8Tpf(Mb+-s_$4)16jSpkQA{X_pI$7+Uxh{ zwjxCVbJ@ikJ(m6Ra(_!&D{e0q{l-WA-Z;pQWV{Z!1IGslx)rBiOwC`Jbc*;~d^Xu8 z68!q<4^yE$wSKz6*BgT4x;l`@0f+RbVgJiU1ZoN!K3Oj?UU(UDUE!&>RPjR=T5Gej zwkAtmNk{)L;rZgu`g#TLg`4GSd6CfG#&{>+(OPjGFas#H+Z!TcTfXvBb&hOZ@9JJVV&A2c>!}c=7lDb}_5xfP#+OpYCG+-?4G52;kEhIpOVmhYR~JZ%YOAe0BZ* zulL7t)c@aCPnK$f=we=}r$J9nNvQ$MH8som)(Crjef`U`Gc&Cel#~aCU~nb3q3`l7 zRNNa@wzlw{y}i1-w{C^sASNcBEYxrE7WTpBzH#qf^o-|%3F-xmn0WUbmjjy=PwV%( zuz%RV3fD`$82mc$9(y)Cp5NtOYDNaa(yZgF+LFk9ObQB$aDMxh@Z~^_ddSgrfqnA4 z^U4Mwy#Cb@PzWhlg>++>7v1tI+P+x#UqXw}iv++IN%G$Ux~mmc_M7AUd}$>*gxz4z zVgV6SFT4kd?Nc<7j1QlQI!8N6(QY{`cEm}OzL@e7ot~Z^O$VAHfT{Nj;tQC6_9g$l zf5WMPy&~B+YruTb0|efA&jHcs|LBcz{h5>$cHb!OReC0zm`Q46Oi5U9D|_0lk3Kv& zM|g~hFLA?Qi4E@+#SG8#kwu)(^C%h<6jK}qQEfCG0lC!y43ju;t8+g|mf%8fxIhem z6o9=K>AHq}Q9Pxd)C-6QbGYw~JCeyrN+J}H{q#O0Rl5-boa2NCHctH7geN2sHKIO*?B1K-I41k~C;$9nY5eysobyV*wI4k*3ALYnw_0bKd zC2s4zt!80K&`ZzNtC_>NKONN8>`y@+L!N#p)2X;I~xH#1EJ25eZ2D{v;i0R6x7^+ZiU7gwLH5> z{t}Zj+srjHYOtkyaIOx)3b?IWQKWm(P#N$4opiTluh4PJ*x~!dv;6UlUeDiubW?>idIs;u+y5ecsLp$Z z#7WV{fB2ZcP@3A=YiMXlsD|KvgxQKOfPVt;3v@GhzAC~uz5I9Bk$+CF7e80ip%{-j zf4Rz!EBk4aGXj94E3d0ciOp#Qd-b{<{Yc5>q|p0#Qzs}Rb{uowV@`uPH*j79w;YTJ(GDef`5trueo@1L#VZI$HQ`LAx)oFQbR;p5WqSTVjQhGup#u6!18 zcAB|JQgfy^9B;aLQdF8VvGF-$^S!MmVrB8~9cBgbx+$1v!m2+%;UMv`0r2m;UEJLB zRVL2-%yO8|pOxGQcyQa@Y5EIS@mDD;mxvmJ+ZY}N%)fc}4kI}yAwW9!hR3pm&`esm z=UjM6&uLmU9_^KbI4p)$0!=oGt+g{!)4DvFw8S~obvYD{_>hqMO&y;H`z+6+rG;Lk zgkA*cRYDH|={=!^&^Zt8y?yulZoc!K@5lLd4!?3GJgd$#Yv!I=_uM{pPodsjcI&Dm z@D`?q{7~isk!r9kbciI-4sDMhPSJttJ-y6Ku>-5f7Y$oEvS6^olfJy;4#(%>=#vtd zziFKCu(9amD^QKv?&2dy>XEddtT-0NQAIZv#g zI20dzIdJzdASPyaF0`_X`>dOwDYExHiGw5{h9l_{yw2LI*D*H*;V9&V!oX<6nCg&; zyWx|QI~$+1SLvTUR}`od@M}GFMi{2MbG0v1^`0RszjC7UAh@?B{=2Qd`Jpk1!OU5= z0&5cdPG0kE$!eqj_nn`FA7cHmDT11YLvbg4$%R;0Ud>GDw~8VHeD+(4SI`*9zEXM?7^>6 zO-W2C>;z3!zOQHXF{=+Fs;Zt3VXE*26OTrAIlpdp+&#cVJlbT-zer4y9JHNKX5_(D z7gn!XS|c`GzMoC??IadcvbH6br=o87%kqjYKxj$UNqTBoLl&hIJxlfg8 zqLPP%+3!CticB~@Y_PR>Xxj^=Tf9*+DPJD>DF&72UHDX{&A(v6?WoliS|m?MNE9?E zu3XbV>AoloP!RD-q-Bv`x86%c*7Vg`mELJJQ8ol=vPMpD)b6fSfk<3b zFtOGm?%NU~>BFwJI+$9q5j7w@dfiSM$5oSt_w|2^9$yO5F#^(GUJ2n*kFTV}`-2v8 zy2=xg*S1e1AWUc=LH|*NVEK#~0+i<588vI5up#>r366pt{L+inrU5bA5@Xd$nKt7No!VwyHEA4i zCv)$lFW$S?w}${ZpVl}Y(!3W2$FC1=QI+$$^?dx&Y_l3@YfDOAl5VWs07s#0En}P$ zF{6~N8D^L@QaYHbz_|UK0-S3RT=Z80@a;42d+B^e-oF_I0KXJ3j(|YLj!rag_~dBu zN^+nu|HM?LX=v#gXU#C}=Q2Bwf*(8rPGaO_hPo25pA&C5q1&epP{&sN0?DXP^Hg=o zJZnSjW&qt&K{-%)r0O)+FjiGnermI|NUVu9Rtzqjud2{Ty_(EU|1+UTGUxTz=k<;w zbd}QduIv3@nlIMyiWM%>mP@!W)elc#0-vBjn#`>#HssPGI+Cl=J0S|nw;sA~4EnnV zDz!!Dsj0 z05cw6whro(LA~|jrbChEvGNE6^as$eBN zxx60n6U9+qyr{EsW@QvI8SP1kX;O>Mo@dGYwyu7e3_)^|3C>{e3jS9=%Gshj_$%en z2sPJo!8Du5aZ#+4VV6&)l#b8nI|};!xNvl7bRp7&A18)O7F}lu#B#2yk z5%<%MiJtXxpXRDC+lq&X?ai%cIX7tRoK8PDV$}Pmvm9Z(`<@#%2_>t|h6&-HW7TZ6 zYi3ZGLnVK6&5Ddzl7!l|fba4wzA>|FKa!brMo4ETMnaHeq4o6-l zP6dggw^y?YM%Bs;&O2btC>WbT~%85+r9xWTS zZ$5da*yda_wUeMqeF<>#7^OG8DRRlZa=vouv40$vLC#h2^ z-#XEZ9|6ixZu>52? z{UzoNb#A9Lr>c`5VoX=D$@yk+t}(NkS%ubgCru<=HX=9SO#t6Rfp@M?1wgnu1Tlmx zx!8b-smjy^YDy&dX{@tSwm0mQy>do~L@>1syD=y6UU^G|u?jqCLTT%1Ry6hP|5~#cr&mr{;}O~yjK#(VQ$Rh)9v+1LXIx> z@Z(xBo)hneJ`$YF+FAClw(bd|TNK(x3QMS_#Jy_zQav!1;f{q{- z2~+S|QS(?Km!UPMf&0-?TYsfFG>6!7*mps7!@^&m*?vWUAGe?$_TN*P_&y=eCS8M6 zDb0U*)`dWc*cSq?4`mocuRd#Kbc$cxq-w(UjG7gpGpIFG_&7S$ogNI*cAQVLX^I)i zQ$_hyWCgxj;H^`#-8DQePhS|@laJm3QE8G)=5_A?icf3?pO^^R0pOO z)z&KrO?9mh&%nFMyu?YpD_sGULTWj9GkWIngaJuI{VJcz{{05lznqMJeY*KpQY*70 ziv#R!dzBS%l;3c;1wAG#1P^k)(H-4q-a+Gl1 zxaKmJC@|=l-J+Z|rfTAgl^gt1bsS|JYPP(w2_kELG$ox79&avD3$R#Roz))cv@Esb zFQ)k#Ua^?Dhz7kL8CW=7A8-nsF9&iS4b;eOi)^&b-10fT>9ufA-870w5qs`KcWwZg*1;MJ4D4V{GxO^k@} zxQm3C%{4%a(o>RAK(}mdZI`(wDC*@U7K0AgBg|KH!gLeXH!}#dO4++mm9hw7^IB1{ zLak)O8}{A($@5j8E9Gx-Y)={_FaKJM{MXf|Q1==RvD5JzW8>U?zwTK2T*V3WBsf>M z{CyRCjqls-McKnj`yEcc_6{EFxAjt)z4kcjHOq523|Ct_ zzy_;jFIc$YRd+|$0|6@(NJIA|h>1?9VYZ$V!fqXE6B||vc!rBZ+u`=*XST@dtlFV6 zWNPANk9T(1ss@Wkw!!=t`IS+1I3-ZtS2(&exoqSKM~$BLp{LHUgg4W->o0+;%ONe? z4Y{H-8h)hcw12IIul1$Y9)v1(DXvl{CaZit2&=OYe6VMs;Az?=?qscsEWT7WFCX&Aym4&mNfk#u(4~%iCu~H-uCDVa&|C zFN%na5gqnzf5&nw1=lQ^>~0Khw@fCvg`x`Cguhp~+gZ2UH#<|m+s7xg=x8ZW>u=lRkyj!*{Sm<6W&JIR|12K?d6q}#`uumrn7@k* z&ZYRPo(Cwy?^nR0mRb!5ONNSJ!YI@vEhvO@zZ3AEMgU?1M#YxTvw(C)8?TNE4!T;C#@p`)yI~tmeHx{FgsO-&t&wj_J7Y@$NFqBlr z&qz@qRgV^Ko|kIkEA7R+jLe^@_TSf@QCRVQtvi7f3t8S}+H&OPbw0QfP*%bEXBiU! z;;mNrQVZ?fZ(J6IU~r{P)lss&^H-8Z4R7uo*Yl(gl*seqI6w9_a6^kbw6yieJjjXO zX@t3IHSTZYRo})8fLyXLRqlGHdd>}OFh+Gg&P9hV_k>}umeKn7FL&XulOpO@x9*ys zZk#~3$NYtx--k^xrBpqQ3v@0mM~1_8oPX4Ql{Gl<)Kt_@n$*91yHm0`*;EVq#iiMc!uB_*j_*cuNthbfma@2XN+tf)nc$(abNeDF?6A}HHWr$+0 z)yhtZg?dU!`N_Mxd|hn(Y*d<)Ygp?szRTyKq|9EFi&4wdgc_lE(8?)XRFT)+jCPkQ zn@RM9g!#DJYh=W1*L6WCc=ieDB8;~8b`cen7vD@{d+ba!!&rfcV)~@p!MTa3k^)!r z57wOR58k}q_*vKBrv2vdc7qf;fLw5a(ayE|P^suwY}?w<=-79sA9PYVQ$ex%A+j{m z`{~q1)GbJ*tv}yoZ^qWk=4jCv%e7X>2wm+=p|(_b_%eMe<&Pe%FJw3JBKw{Wu3}oz z4f78?;ei;=wRgF?1;&$md$eG$n$yeL+1dHrYlfXsBMnt9-5)fROJ@8?aVE>94&Tus zA*`B|$l`Cy&W!yVknmn)$+S_meOa@1`XQC<#3*QH?6j6C~B8RHJmDTcP7E~!S%rx=g48-jVpFHW0HxMlY%22!OHO8D)rnPNa z{L7jBw@+o9FAQ|<2N9Npg8U39xBWA_@a)hI#+<-r03NbrIQ}&>wYS}3m#b(6ddOeC z;L+%dCQ)JVgg02(b#}=ndZ@fU_GGtNmd+DoqiW{cn?^b}PLP|@#L@3?z%$gpy-TAq!bvZa` z$W~vyK)W$DJWJP0cg5A!u@IzV5N0*btBi!$PS5#ad+AR66Y}IseM7l_@{3j6SE9tX zUjdUBHAw71HIox}_n1fOOBY_ymlJ~!I-nHeCQrV{Ddo->IyXjj+u;RS58%Y`O$O|& z;;gyfHERB4SN-Rwei>gsbBDmG9V{2v`{{*P~y6#hRJnXA2Xv^=EVs#Ko!ROr0xz{b_h8TAn^Ut0EbX* zVEYPVO8eL4yLAw+_0O;6Ug(yusmT~vqM0Q|Mb)A6pKen-)GB8eJ!;&_(rY`cNUQE; zf7ss!N3#1A^HGurP?T;l&KF!M=kR{h8#V-~+ME}k#xd8))1`}qbAd(QxD0p|d1HTm z1TNq90!NP-T+%Q<3?*nA>ZJ;8<3HSwh}ZrJr=|N<4XYRrXZGU&tY2&V-|q|j=YpKc z$7r796O)g&Cz%&D^N#bfb1?1gY=j*gIahl)I7wpRS)H#f#~6>Ja?}qCkRE1J0gK@M z?ml?AB12vd#&v!dD{!AyB1wH`-vgw!z?$yomOC=Ais59OANsa(BGE8$Tw0+)U+He7 zu<=3uN*fKt^V>Tq?)S=Kw#)oQWpZTKGD7Su$RCf3NsSWJ%6CJ-kjJ?H~jSlI0& z%n@AsGapumVdZon7W$b*Z2?ivwf5x5v2>G!Us?xdgR1my{XsG$3Yu({y^{2vXuIoc zS!iJI9OGQcAX;PUKW9}2YM0gT|NJ1aDa%V z*@do)D#lQibh0dQ`6Qxr->*%)*5pSpR_SzU(J>$^j;=JItQ^BqulZ$byZL!sSoJ1d z=6v*?(v2T2!#|~|e+l9lJ8TyE!pOO4FTZ1iXD9Vd9LA+W{>`vL~gs zyM=?>=}-M0cAGF?Y?2Y*NuD|xLA#Go`4LZfTkQ587TtE6tJl;tEJvI4wl5qoZnNfD zdCl2Zv8L1a8t{TAMj+KTs&ex430+)+>Q*v>JDUz@m`}4CkX>@{^irY*)EUK$fZ0Oa zRZWw;lVbDSUg$X5RJ7KDn7`A%xJW#-TU9AI!dY$Btpe0kg=vN?S73)y`bJ+{>N1N~ z@5spG64%w~StShTggh99?gg)myO(a^cqf}-`0K)|A@I5H+YaixBc3&f3Zt4L$~y=T z)-3c_M?C!MPAZA=#A=SbeEg%K1Q41s;riyx=M&OQt9m}0f=p#pVJ{IKic@`v&)FbX zS*BizY%qVJ^b8rHQSo7krrj)r{}+WI{$=zOv-p0v$Pf(i+=nTr)VmE_ff8xTkcJrU zq|kp-*v@C+xR|h8U$T)-%LObdpmIyCO-|qY(G@7Tviqf-9Bslx0z@|^n@VzpDdZ2+ zioi4<6q096_+1h+`O1qX-gaDc%{7lvAG{=r?Fjp}QqN&apDeIjS(6~2X^FUkB^Dy5 zodJEe8snWJg^San%+l>NUJk|KU5A;espSc<(XY*#YJfq`*=-A9>j~IhsZwu+lg0Iv zD0ai0MJ!$1;du8CF99JD5lg6zh72`kZ$A%I__D8bq2SiebIR#r`yB8V4|MY?3t&4p zZeHr1Y@`YIp*t_EcE`vAH+tSQ?cr(d0}jp?!=5Vo>UV2vy`qPdF9qz}_~CW=fnf;@ ztvfu^q^I&RmPIbAps?)Oo5MYP`i48xtMvEprOmvn3AF!s1>v_U8O+ux-%`aRD_v!* zTF6Ffi=qpX|8kY!!_6OcWbYy!+fM4KvBVQJN`IhOjlP$X>zoIsdsr4>Hh{wVG``-) zPoJ>aq^}k^|FY65x!k6WStjNIsAQmUPC9rb+*>+MP8;@!<&l-Ll8RgVSnYt85bf7- z6vMKcRN-OQqUO;?EMDGV?J(?{cd{^iw(}|&d60_9NnoaR_nJ4*H+3#}mC+I|Kp)pX ztU_ga7_Gb!e}@?^?rbjH2(R-tY1nA42{R8>FY=hLJh?JdmJU9yo#@8BNZ835q86w- zqo$#4qaiNZ-j;75zrxMI0AZ`z-b!7ZJDBf{tU*JZF$Hdnx9SGD9p*Yo1Zc+`R!#kQ z21P+rF0LsnJ54#&P=jrr`%HsH6XQu1^uS5muMc1%F0s5Lf7C(zw>KW(8zj4A(ugSR zVQY!Fs4MMfFl8L-jdR!d=LWvc%0himp0#>7Vc5VXTa50f@OGEOuSCiQ3qsug`-<(g1 zyz7`%E;EeFkWlHm^{u@UL8pk_^XiJ|zGFI94hm215(v9iX=`{ex2U-$ym?6dDNG>w zhbJq0qb%9Amn!P3NY9SKj%YOydhedhR_bF(E$OG6(vBG{~4#wB4)44_N&eF(Ch6BV_Ywz>>kkAH; zNc)BdqNmHD%k(C$UvSlU-3dG) zVmH*+zBA?6kDi7U4*w=UPBU65vNvD^RAtp!`_yZMkK>rj-pR zFmHT-STnYlNbn_eaOec@1vM4J#hSVC05uxuQq9>Gh&cb_l1obZY%Mg$#m!h9OwSN_+wKa~GHkID? zd(?iRDSXl!I%XurMGqmRAvgWZuN9XvmhT0s-wS^>R@%WnMaLaP=rXLO0Yd9>L3ZfVAGIi8KX@I5j;#>Lq$I?j(F&ku&~ZTN$OYW4?pg<{@$JX z<5gQ39uX*87CCmWyXIo8S399s;1a*ysxfZB3ZMhyBVBdfW%F63!A*)-u9#j<_K{1+ z(w7dLo;Rji%T5{i#-~uk;Rz=Yzd)^so6bdBdRMvF+ZTpj+Zq;4pr~n}IkPC%w7)3Q z+pbZ*tv@%=WmJHhFyeWS7>%^HP7cmXcoy#&eLKRb>gwu{?gu>UYnQu)ATn#JGW@(r z!)B_ngpwvt)(6z8%$f&@Z%T!>x!UfjnwTc?qWY3f<)^2I1V~WbipNSVrtN56=k*XiEqe|ch~-ejV#h7pSzWt2@mrp+m^Q!)!rAE-S^Zj zI{FbBY@LzFdC<75H${5flyFKIPbgO9X_B}gD&)1+JFAT|OUBRxtzYl#o#OXu@69@? zaQj#*qpnzhEv2`m{t>h4R#T%MA5%1PI%gC=qN7tZLOn6ajWdagKN$3c(Ner8*c3~N zSNWVh>?BbjbFkmBD67J1KDfVLNg;epuA1#I zLt(uT|0n9!T%;)*XW3cS^Aa(TH&G)M2RhFlztfN7E9Jp|yU)eJg`Z;N&!0h@?Op6i>(UM>hu?jh{l3N67d(;Aiz(Ks zSb;p*t*DB)Zu1Q}N_ykZm&?c*H`>{Kabm_nv3Q{I^G8lXjTNN}B$^|aO?e-A>FXc+ zHwtA1NJ7xTVFMJTKlSrMgkvKj42NZH(4F+kyDGTEB)Nykuj){B@!gpq`e3=PJij@N zhP4R4ILo7z&zU0{(4nDA&sK7+#5lw>WAbtfMncwZ-DW&ieOnW`2}I zv$V)$VVLI8Yz{(LCscY-Q&ast(GslI98;IdyO7k0z%i zjwN6GkVM^ZGqzXn3yUoqDqg1aFIB6-xDkG<=EnS0Z)hl$#KYW8sq|$W!U1|~!5iDV zu7{C9PD9=o)829$p3cwf&P9DCPaI#C9;Q_wQ~?O12ZQ{MLXv?RS%$qD#r}f*71|W2h&&EY?FkSMh`(G`sussVVwIjTlBKE$Ng(bRtEA2WdT}R z8~5%$MBQA#hl?eO+rm6v!Zlhv2Q!VM*Aqwt*E+>B&B(2AiL9Ccjc)#x7=hoqGf z*t4$Ij}8dDs=O`e`S#WQr$SU|dE70fb?k_dqU*P+TOXU^`Z0WGjMWFJU?;?CuYLOr z^{)U9b+r`HfSR1^mVYQI8qJ@p^vaepG^H8%Zjn3 zt=^ai(~Y|bzCMcBsl986dX~hu=8GjyV(aJg{yeC+HX{;yJjJ}N!IUx(()ebx)SGp) z$y`F|AqRq){Z6w}L;23&^!S%Avx;v6bVMq6!o}R+d-^~LNQ_ky%kq8zOU%&mFl+H>_#d5u)gfl-P1#Z?ZqD?Cl9r0*^PhS!}-IDxh2$32RT zee3ZVzW334AS*j9Lpij$;cC)IX((w-`Ym_XyydN9iePELDzl8y9+$>{sAq>In}K?y zaOcMwI>u^K>h=h_IfX-)c?xw#p+21v&tro(p3Sm*r$MV#6G7b7_fb^Fe^%bL)A9|R zI+zZ~MojHt($Utpqt#Sj{V=qv#Wqh*Iwl*qXiBSxE=Wy8rPjH2viKu{So~rQPK>b_ z#Vi+&0rh~Rnerj{xK4`BNn_l^P+!cfz%sPD zGv3DyVqkA>@LavLZ)g12GPS|Ob(YqF_bEnHKr?nF7M<`_8VR;dFjSpB9UrNo*fH2^ zZ4R%iur$$DPue_M=*)BD@`^~@7|*R6$vbqQ{<*5{wEhl9jLy!Y!0W(s^@9bR)P%v$ zv-BX~P?xteH;3m;B|OC6zR3L;P%lssh*Y~(Md9VA^pww_B_H)%js7tg161pxO8M6 z+d`oVM?Z(NJ`dm2a7j!|l0NlKm$ucY-hAQP5jAQB<*3ZFG5w2JS)ch!NZPIAEs73- zhH3}po}Z)ruNe*kvX~iV%$UAkif#=!&;&3&QR2i-A|)}yo3;D>Ro`TL7f5W!!w0N!xLUUq$H~8Gs;H;o9b*L}hIP=a@ zYFTaS*3p4`9b%OY%XD-SkTL7fAQJ0g7CO3_ComFb(Z`35=W~M7$Jz#~^+l^GoKAUA zqhG##R1rcyy1F*;b#pN)=Tq14nh+Vx_B8HuPaw~gXXfR#6P1;?JgoMt=CXe4C6z5Z~~F#e74%KOnH5&@d9W6phrrT{7PHua%F z_dL`2-SJI)>dQ~@G|m)Aj08`VWde8~o9k6QZU0L4M%r7401Q!AoalWfb5CQF57!JP zffC?5vtRSyXReYcEA?@F-~lc+jXghYLdJUCoSQW;kDrRP1g*liY}$C?1PauX9)`*X zb~a~{L4@=sCcE~zYbM>))$&tt`cvUTr2e? zv{H;W%k}h=K$FLqTl6r2H|-)B`CLhlEMHbb z>B3Lv_z*%7wE)cI398xAh1WH|yjFyhzIxUbZT;{PO{YT_KRq>)3@4C8pKoQSl-{PN zgDb~+dvL68LUefp8s(@fum+}CR|%_xxnpxIJ6WTQo=&f=y)+jlNErT}GyNff$y3zQ z&gm^w6pK2PCZKK$CVS*N91ws4q-_?Z$FsoIw1@kd>jw4&}C_w!}>#pyeQT`a<|J^+L?O>0-_t|2M7WDE=5lpH!Oce2YnkeC_y0j|7|Iohz z>{E&T0r`N*J7IRQJy5+mPX(2C)BY-00?B(7z3)J<;C)dh=x%z}d*5cJoxP<%sLi@X z_3~1{-RU3ex!m^^JPaNi%*Ogy`B&!Mu>SLEQdBr0;;t*V@IXkcG;^`T!M8pcPkBnzzJ+1C zHQBn$&1!5)PXP7ZP-0Bw%Vh}dvo6|C>My71DChLkW0nio)n{+Gu;Q4Hz!u%}QuK?r z%=;kvoAY60Jn@7YAePboE_lHaRzR<0El!t-&a%&0C~F<};_J6;;y~Z7V90U;!Xc^+W3r1e~U=_pxG}El;dYthtRGzLXcPJ;pR18_J z1&DZ^1Nd|xXhm|!=nU1|I>|l!V->uC(8x#4^ONi~IPY8}Qroq@sSDBK+$!PXzU|L_ zZ104|F<#54nyI-3wv<@|9wG3GQU=LV$_+seEpLA>8q2e0` z`pQ1F8fFl_MN9UvXrByybX1No&=c|k!kC)?1U_3ftq39}+~}>E-MYJaLzH~u!Ua01 z=T9HMjP@|y1a0}rF52!Nhd%c(JP>sogfUS1^_s+fFDEz3Hh?!H%ePiC_7sawX0{5% zNS*Rv=~V@MyUT#F#;X=NC1C!&cVS$K4lu5ME}dOnJz(=WIS(Dys%tz@M+e)i#w3*U zJQRGggOYf9DNu$L6m|8(d(zZSr+NV54JZNCYgxM}fhZkTOsZBen0pNCm4eRJ;esA3 z#qFXAtSeSMSKdXyA3_d#Q(8h@PajjR+j)Z25#Jv*Tv-1pV#tn#six>5YTH*Km_cXI>ds6c1X5ZtHAyDK-8gKhsl9${ZZT)|){>e# zh7mujs44@Zl)$E`bbDN+%(Ekn&E(F;aM~7?rs+;swpUh3^MIrLlg?Z#(Y)MMq@Sml zQ(f~#0IiIiC1kF|b+I(%lAPE+t4jQ}sd*rG;y1hu7(r zk}%ss&G11l*e~533dj(@&c}0a+&8XV+C^!-Cbt&!oVfPLZ5a25)5>J8Rc&c$@DdVS z{3_acm<`Tvo+OePVqGcSzB~9*lZtDo{|g1%B*b6>yCfm~#w7946ggR{BJ>4kyU0|b z?aetei{4TvD>GT=5u>kv9JM}v1nnyOW^4=@9p%hdWiUisHP_n*$zstvAmT85&J-~y zdKm1Q6wkz_+`}&SLWW?C(GM$pY>a%FddF=UqP6!Kv`tZ-6o{otZzm_a)9R|>;b3%ogVP4Yi6r+5~F$r2-v3h&>-zvhGzpVn2yzWcb0ASO>l3%KgLASodJVM4E=iz zZR`lQv^+ggPPZ|;`LVW6<5 zi#Ulg_|dCzjPb#Q2(NRBh?iC=I8Q{;@1ipO)D*N3h%UV6QS7x8zQKsHxtIC7jATEj z@96cZ6$F&%z(8#Oh0e)GGRVF?RZ;JMNkkT`lWP|%eiA;*augWvvxNMdSIuzGs$`x26n ziKM$^&06kkx5Cay+g?w)x}TRTV{{SMxtuRvqf>>DLpwK*mDl&(7LCxB0X#GOMx#Y_ zqG)y73_vhYyA91EkT^B!@s=0qDNoNV5sQEME?V zaI^yDwp2&U^UgCVrEqy3;qtlt$8;NkY!tx9XD&E8+)CP>lP(Ezng(IB{Cj~@cgmC^ z)0oG1W-GU`TDY!e%$L|KDoXRvrIQdk z0d2XqY@p)hpLkFE@`KL#kKDqBdQEnZtd7{5z2#2_M~0~0ZYhMzVknKOK?)vkEE_Je zv-FbHpFD1z9gf-9*}WCpQ>C46unGHW32JcH92^;8QG0I4oS2vxvJd^xbi}^7f5`QI zV5Svac#Qkx`}gIMIC7p0djW>Ke^Ca|JZo$oDu2~GdO|(OdFWB4nY&y#D$Xy#qONVY z6jYr9=k~=>`Nj-dSrHNAzbnrA0A51=g1|Qh&!Bd-XPiQ#fT?xI!JJb6BeI` zC<1GBupdjbQe&;jKtSC}~iu_}R z_G74g<_)_Cva5vn;rw(C4lev57L~e`-};Ilq~9q#np3ym-Ffwh+Y-8jguG{JR2oij z;g+^G*p6sd04Jix##+=vk_1PziY)Iv6zq1=^gRB-X1w!GJHWf@!Q%dvfS}KnzVX~* zbqt4}YUin2*qZxq*3}yvZD1h3!28-8PPb_j?wA-E`x$`kV)s`Y_EWNRhP^e#k2?|m z2{l>Tu6F;vibLAGp4c^_DWMm!wDoy`Q?(u+ni($qAr(HfXSXP21tJU2Q(&VhjZtF~ z@DLcY>_ff!Ids!dkA6Gpj>$v4=dTL2+h(tX-m;a81MmXsFh1U;mV`cu2cA(ES`$!S za!>Vdi`n-&V}tLRI@PPS?NY4@7K4&L0x2TFuXvv*bJ5?;avTOc!9sKP?E5DDKi_&J zUw@ohr%)QYywNTQL|V5>;4Sf7F#xl>l7QwGH^$0s!|4rF_ee8JM(@R|DcN!-~mhs8~=37{W7z`3dF1kNlVR zk6~l)14>?ebeKd28>m86>kW8>P2dD}VAdw_bK<5x99JJUrE*L#o||j*vmaNJ&zGi6C^YkG>K*A!iBQD@-vImo?pY>77UKJ-ceWh88Ko9MiqDk7w^u6A7Fs5K^;9y- z56-=vj_Xei+gL!?9;k4t2+5b@Ifhq+7=gdA>oTFR6d^VETRlm#>( zF^+UbK+1g7@i*M2a&51K{+@TWVWSEJV)m{X1u3wXKG*aa=eSNq$}nfOd@oCkE?)sO z5!2GurrgfQ%3sfsHRh#FnN2BSp%9B9!*Zr)8lTgIuhNbF-lT!&(W*q}h|)RXmEQ-& zXe@9gd)HZF`G5GJ{yiX1I<{Dq<$u=SE|6ZG>V;Hx{|os!_s#r=f|85{NJd5(^Ir!2 z@8M|F#kTSGJD!x}AfA)yFIW-rM}N*&ozMB_#>6`#v35r@lRB z2u6rAG1xs$$DFsHNe_OmuIh5;;T7BZcy;>&KxI@Y91shknmG!sP{yhe*GZEHs zV6eD+jy{m=BLoO_=HHD6&9aCL-PgTz-6GIunaAY7x;&j#>9?Z%TmQv3fJlD9oJ{{; z=?Q;#6@8N_0HU5Ns``3**Bl$^#ESI?4L=jZ7IkF^uNQOR?60+(KjYkr`_va>gYg+Y zXECqxG6TxXk#+98gb%?CNfQ&39)Nl>yrVZZ`sRBvM7`s)E5f0YvUqc)3@LZ3+Rf#} zb!iJCx6dPsa|c++o0A)^l>Ixx{>S(mgD#zs<(11h?vy+SvP#%lJLLe*6}&r5({1qBqBLKZUWG`1Cvx8TAK5#KoR^`7BuZTX6pT zBxz0%a{JK}>_P =)=n!?r5+xz@mQVGD-;bbjdLV1#F$-?EUc+B>6N7Q? zNqkI>8^(Mm1#e%W-CT-x?1ebb)>uT}$Jr14XSXx|V^bUJ@$}YfJa*0_47%XEODMYC z%@a5~J3DnW&jqO}&d+C;44Z)+Y*)0voV!}SkdjHy^l#p~$Ex&Ze~*xvIcwMOJr8Ly zsc%5QWtAM!D?*F#@0(FcGfTefY55tAqwYeXB{X9njieax@y{8|8R2+Y9^$$AKgJ0R zTUb}}vJw%k@J{q0P@lbXZf;IR?zoq`Q7&wSNwZ3@rjh{smY9hA=yd}w_vnku$dv^8 zX*CJkiAt|v3a%5t%v#6tN%o!7GA^+j(Lc8HY^m)?aVRCrfpTfev0R4r`(x%QNp3)a zMqdmD(|W2KiKA6L&=fq_^y<=@a$`$NrY%r`p)Kl%BZZuxZiHL~ca~Y7X_B}_p1Czof=ED8{>YwWmbRSR9 zej!idbia>9u3k_3yz!rX7kJ&y<-Nhc^K+Z{uO0%jn?3@M($379{Lh}FkphB8ZIyjH z=g?~Xr#$~nynTrQ(`odsxYB=5a`E>eH$FO}H3~UY;5tu9{rx}x*4J6;*8iiE`y!1~ zCpysI|8vk7pZJg0$jBd;39TQ1G?ebsh&S(VJt;F@#ty|*mVw+ShURldDz>`L@xLacK^{)C$pQh`&!>VO9f$i|^ zz4>$7+uTZ(;;*)iUgL%xowd!sYn)m24^3^vgS%)dAAS19K~Yf=1B=suzxXAOf#e_2 z+FNz2xNm{RQy(vh0`2veTu8y{!ltkOZX5e&+b*YDP``h)-7kepx#B#MuT7ToCtQ5T zs0UYZvNQy3Iy_-EepW(u=cCW2xk+S7lGq>(=U?dvKvzk)isBckB&c1?sohs}F462p ziH`h{KL2+?^Ptema@oi$s;5f(zt=;$Q?ZVV13(-q)z#It54r8pFuOwn$at|m&b|TT zR4J#ERDECvmiw3Q>$`xrS36Zp_e>< zxYH@DqfNqV7_WCG3ciH6o|B82zXvS%`9gyP+v(W45jGKZjx~m4xpR?EJc=R;helJ+ zzGb~L<}`e{SERB0m$u+!y)%`-Su5LNBvsXp<$Oy-bo1!UcPwLVTs+or6Z|~^z#D3i zICYZPC$T+r*N0R8LrWHm`?f_fnRc2biF;L_4(6zV?=STq*xukcd&g+hCF_|xuuS`O z&fg^K%{#E1e;^ZGm-cGGab%glHCF=pPwZkVLbmZ$Iv&eE`jGs>cBv#aMZgKXH*y1pxt?`v&s;%Uk#plzzGzYzjbxObzpW2;7GOYvAJ`$>Z+4(qgG}w zS2Uka0iv(3=*`;bSC$b2kM-%XQgd^n+Yi4qjm6!5Iur=}Nr02W&HM|} z{||KmB*Dw)Ea92fscZM#BaAj`76yaS4uBCA&|7^6^J_%~3nhVjxN6eU{^47hA3!_= z?u=+DQ!fSWIGHNSJ0)kb>T-3%t%24e@wXqhQwA)$uJ@G_`sE@`S8jx{?FHcPxwO*L J!Y4*={}+SuLK^@8 literal 0 HcmV?d00001 diff --git a/docs/ref/internals/_static/execution_model_2.png b/docs/ref/internals/_static/execution_model_2.png new file mode 100644 index 0000000000000000000000000000000000000000..aec1298a99533e3e44b7c96c4ccd25db9464f1af GIT binary patch literal 371519 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%0A)r;WRE65q! zCkl^2{!5JzscJh+hFR>V>>-mULxZQivq5z@w33%HESB6M#jf9R%-u7U)ljG5rpB#dDNz;&L+EgDMk8Q8Pl73)V7fNsh@ z5Ft+$;LbNQ9QaiHWPG5w_3Va7BrVv)bG&N3LJAW@Hdmm17!#F?G*e1R=DmZxd0gEO5w}!Qh!;d7 zj6CCuCB%!*ib333S5=M(ZkKLSAfl#A%P&Pe#r#)gry#=A)Q)p`J(C$`M&~uNCAx}p zM7uJAlTYogon6BBG-g*=CikmY`lj{CN3!F-vr{Y3_DETj|JAqFS>NdQpv1=`KjB^F z+_D`e#tOmw;|7Gg?!^x$I=uA`62}vaY%bF3>+aY~bX`>og8WVS6|*}Z_5|pB)cJhb z8A?8q6L#*D69_wAeHiN6>mr#gkHdnhV0mOcuo#DRuF%ldOHZgIBQiD)79m=vb$Q2m zH(H1@g0#vN&v;xP!Sno%1<#m5(dAjfh8uS!jhu_yntUi>vi^;OL)%9RuN+s?>wN0!PUIT{!!BGe#UN@vCwf7r=c*WqOu9#A@q*;A` z5jWt!iWCo~M+IwYRW3nKWr@A2(6J(n;swAJHLB?_K!xLOk5#2-T|ij79h@K)Ly<2z zsN9RYCb2QBofPU6pKFIJg63o1a3)Z8c69~yPF>5>_Ta60JUPP_-*$edcb0fTXeC}A zNC4Ad5-YL!GfemKJCZKC7ZYI4aaPEn>qKKC$~`~5XMPVebl|a%c5h9o`5gDv6%=#@ zwqN{mJ9%>wWHwXxkv!R@tPkiLPEE2dhDN7SRWMTC$2~>Oz%bOa95J-BZ(^KO?ZLn~ z7g11NQSLa)uZrQ>_6Rwi)y?!PkSsf*_NiL1I>D}by5^NTw=wuQ6)9W%Y%^qH$gX?3 zT@alt8@ma)%Y0RLo2W(~I9@#KzBdz!x}$iBg(4#Il;9ujn2064cnzGvm-i^iD<#oa z1;(l?X7C22Y)Zxud zxu^+wN!?6ve7B=kd!pdTcf0wUDJTd>#Ku&` z6kWl(G=R=W0>;X~!QIf8r5PHaarNSA?IF7V2+YGO|B!-GRO_krP{5akxjy*@*_TG= zJ2{_dUiXP82b!g~yblyv@rx6{GWwhd7}u1Gc2|^ltouT?vIK^v4`kl0^Dw3LRw|wk ztA|?mUL0;JzdaA-EAPehD~3#DCFa=`zPoc%Rq5nhR)v@xSIz@kE9GeXY)IPT3ZtVU5n~A_J`pqiZ&oe`{?TBA{d+k)}?Y1RxF4W;3dp(|srqkel)g(1) zXdVUBm`8-nHr5wU7oQ4d*VK#~cZ8AhX*jM#swu~rH=SjvnV6Uenu1D7`t5<*=w7S6 ziFoM**2heJRjaPISSJq(*~N^j%Il1Q-o%InB=b{^U5|HiD3<7w^aAXT_9#)X$KCDQ z+IR$wvQe~_K3AGLD`B;gIuN;?hQ@aVh}_kfZJewK2E!IOK$9YPIp2GzODX`3#=ukp zPXE-KlyDdc*&6UIjYsvE=JBfxGzu6Q@D8+1)$!}>RH9qTXmsKxLKsL&t$M|&St%p$ zs78>Dv>}^M>Qo=-F`LQ|+(kaXyf!uD8yvn`bO4nvsMt07rE{!pmC#Ui{Xa0AVd_&- zQa!SCeqkgwS0rIkX!vHn20rT~al_TnuuJS9HY&e!!L4Z61Oh!9&x#0G3p3QT>k4*T z+^=nX{hB8UC9hg1Mf?5T07fUpuG|_v7L)M@FN}tBY$642zOf6-54a&*SsVrQ1>j#< zU9CGmJB!S9Wec*`uN5pG}IxzD@kFQLM zt*7*nD?i788?WEOppk56Bpng+R=Fpy_4Vl^Hun&+#$?{^w{ulHWuT)Xp}|5AX);?h zPrgg;A$<~s&Ew`0mY<(5wfin4L;~XXkmT;eP3`tzv3}j$@rWT?aznU?&1Rs0QhrA0 zkedRVp7MR@5!U>Kygi4YLzB{=5ug}IF*9B^MlR6G|QP*dUJ`mC%A9zxgGlXW{A zOfDJNo*B|p3wPg_WhrzjA|&KjTZ64Vx>|2inxr57c0PHK+4TWM^ou8`UD~CS?`jij{>{Ds0b*`Lh zh$K&v>vd6n8u=R0Q<-DfO3pbc;$kzDoR(3dCHcbAL_2`BhF$0|OeRF7PShp(iCQH0q7QM^obD~-pn*^G1~dQ#l4E}=6sGhLN1|3~Cm@4dYf#e{_D zMG2f1ys;odz4=E#Hw4T5(sshGj=nyY3vHVY(rE0=fo%Qy1;F6mG&AVytFEgURo*V> zViF3x0dI@7YeZm%j;$nfdp7i;fT2ja^|s#AP6U=Y`p3p=MUQ=sYilCxPN6UjA2qgE zA{A`O@o)SlhyfrIQqEC!zxD{8e+myr)9CFR5>L^~L)4$0E^T>U>MCL11=LX-gfKZnVi>G$J}F#GEoxMmgW3b@gtV>^8FD8nwXO z0Dplabz6UdTrm-*lo#JUXr-xvXF5}+2)Fv#2_GsQ%R{CNez6WVUypgWB4~UwAwbrB ztG$DAXzM3AA)lN1Yo|5N_5PPKBzN1#bErz}cV;E#fDF$fHNUil+uiteMqj-94AE|jS{Km4a<&N;HL>6=Pxuy1y>WG(-_2go4!xwy{ z@yuU5j~%NCows9EVAV z^949)WchDo-pLq_I&Jh(QmEwXDI>Hpb+%luej4hqdr@F=hSREbgeUlVr)!c?#I=S3 zKJLazIoaE@$yvx6j2aDS>(_WO-Fj4vif-z3Q)$+KTx?@wLy6lSGL3zd7feh+k#jhq zuN#r~U{lvFRK^7h1QfUD=t)}EOh?`|0ZM2$a?Ndy2|os0oTH=1MDRT{lIpTLdvn7g z2M75sQZ}e}C=4OY!#1!b2_6;-$GxzQbH>6f02e*mAh%l&Dg8znWY#i|d=i%6s$ZGV z!^0Zch7o7MV*4UJO;o(lbR$iGPb6%}!Z^^kI^STd+G@0NDZbagcxT4UfnY}S%tMX9 zw{ke3P%*m|FT}ynrtG%8s;U}QR=2IKJ-wQKDw)MSLH*!fmn#Wy2{wF?Sj)cbp|M3pa*Wkx$3tL z?0>8Mf+x>i~=-B+Fi%J=1P@3(5*w{4CA3ax9hpv_((gHt2ywOX*j*$3CW& zmRbjF>99DvL9usaFcpW9h-r(WhOGK){|HN;9Ew>Etx-h3i)#Io0etpa2J4WJCfj5Y^>9C)c0?y?YdAYcCB%Mu!0>?6>Wj~8GL7O}GB)4u3-ZN$ z?m}#}EUN(T+xq(W*A0;kes(N7eVSN9HGBsm!Ysay1=tg`0<` z)rWa8J}N*pCJ_(Gp&p|yI5w@NBn`BR8KbexR$aNl%55A2=0*ix-7~6}c2=GfHly6E zi*3PA3d#pGDMyAEpbSzj-a&wBx(XB8#SBbI05^p}!5wXMtxm;IWIiC!a=r61ec|^)cW=;dsgKj$+R@N| zA<$sgyM^T#x_XdoY(xZf{r$_e6vTM$LSZ$0bCC9*>%Q*^ry$@*!1KkwVrVV9iJxwjSld>%cjYU&1l54M2eer;!N;v)|`fGW_ z0*YpsDvziNC|TP+BOolT^Ol&y*bPgU@D&nhTpps2GebZoFvF$w^ zdJ~yBevM}%$4cM9@m?~Z`+4~h>o(B%^d~YlzeG~ieLi1FckclY%sKfDrz}p51K|Bg z(p4f>A3b#3;C~h5qgPi`SL;V_Z}-rZtb(3PuGGB)HpB1qs)@CB#YJgzzG=lDgD!Hc z*gJdo_~Zc+Tk8{+$~QU0;;_Zlg(pRB+{%d62O~txCi#^3M_skywTsm(+IM}OKHM{p zAQrauWafJJrGJCqOO$sc$rl{j?wjyE!~mt-ZB&|)B}_#aj1;$eZBo^b)ST2Nl|?sZ zs+%S{e0jrB#(~mOo_mg&dvnZH7x?BEqt-}>n{+7x@|*`FpHo|LV&U8p*~x6&Ai4LF z78W99?hk_uq+&jfKZc#yzJ09So-r&d`f0-+Y# z+`-9I*Bz9LiaJD8ymS4zohH?^?eg!4QG%4mMnhuhm?6VAck3H;bf4w&RN!@C)<#V8w^Tt$Jj_^KhVe)8HeOrnK1Y zB&#RcT56;ogF|wv2d~@*9AC#Yy-DAOXsdJ(cMzv*i`6m!our;T{p{kQFEi?8{5a*d z1&=YBt;WE&C2Xpb5<)#J|a_fWVAj4nl225PF;PR1mFb_V!1MJ>~}PUNym zT=MBBBg*wP=08nAYSW*xjP15ESj>AA!Z6*B(pQ*BNWW2HKIXbRwjOAZ>L+FccrQVo zSIo;{KuzuXj6~bM^&T0EO<2b^5SPnpw{p96jvGgv9#`9Tqhcwk38FPx&-Vp@E6`@z zE6`{wFOnlmdM_6j0fU8YE(AT%2RN7L>*a?$HK2<(lul@0iCN}vW@TNO<+ZkSiHSN? zD9ES=JgqwFz2|Z=K}+-XOR+7AX#L^@XGSg{k1r0$K8!Sez^X85#NO z*UnC-*|57ArNO+Kn)6E&6BD1{-l{Jq5ecefOX5*w64xsN!6{0s!GPE50-?Jf(_aCI zh@9v)!7NwGWGFd};VU=!(ytNeDtejdvLASrq-CitR2({PyrFxD4(a(C97W8o7-wIk zOjjmzhu@m33&3EnKpeHn1Y3F)udL&sZdI=y;a4FGI*;xtwZ5Ipvm1DxWNSAdrbnI2 zg|gD&a2CKApL6ad54%jX-z~XHg9$1T2pOTbvKbB4BDTmu?WN;i-Qb> z|M1RjKblgWY)_t*+}(dR6s*(`<#@C3d<7+NDEz@+`p-l@#WR`Gv`XzLH^$lZMd+=c z_2Tuelwe&Ss{@7(J?Xro)im6mgs&QvPml}DA1(vEpxKcDw+}8b1DRNp1@IxZvhs5% z;E`oXBomS#vChi|J4WIPYz0} zIz+xLPOp&SKF2NCLr~mBkEKwJ#>T#yH~-d)1$+>>ym~VhB_%?JAfhVCaVG43$c1Y$ zx2sweUcH!YE{frId`bVhL%5PtoZWX&@0+RikFoEQ^>6qKV%0i2u584w;Eh}=R%2Zq z_+BcyR?WP-7+Q%KTY+KHU8HUYu{?_!vRRW=2v&$6wd9&}SxGE9NMg8CQ$Xt~){a*$ z#-tW){cRYJ@;lEHtILl$x4&!Z@214B$_SU@fz5(n#lW86)kL$_W+E35XU>M5A3Bll zwMe9+$jBhTJDmuxKR@~8N_ASw?R|f)cF)%OtnT7ugC;TQbnW=`jOe(V%O%5BN?AE2 zKEym#A-|+MN*8dYCQOwWP>)VZutui?* zcT5_n!Y+iTu9iGuFA|-YHV0)*jfMpzS-%hAHB)i|ex~mm^6Q$vPvWNs&2`tf z{#f|n?;roHqrNQzctf)|B63L5X&HrB~`29gi(T$f8qemHx|LN}@0TbQMNY5erM&y5;+s_Xa6@Z8m*hw1q zKm6UeKQK|dK`Gfs-{koRdVgQeZ@w#_25dZAF3NlV;qSIy0~6)gpvd$8d5eBt&Tllx z=rz(&mlWai%kMm9Zr9Y*MC1!cM^C7Y7_X zLZ}6e;v1{^!H)_yHi9A=)`ynLTO-8+S7N$RKRZ1>5~#g~C14oYD zKgV+p1zM?_ly#hQ51d4b$wvu4)@*?X`VY8kOjtzb{>+5^6u^+occ{I8RnnR~@acjf z+<7C%>pnWB;Y{$z!&1ENSL-|Z?3hC-%je)t@KDODjt?G>?yT<+!CY6~qcczFxF)Yd zJISa;G9Q$@g@eJ;LB%FDow7f(p#eG|MtI(luYRQRH8%r6T%daQmzvtz$Q<1E0ujti zQr%-2N235>TXUcSGdxC8C*gI)ap^|WeL$Rzi9oDb9-=R3U8(2nFsdXIUX~!|p`Z8< zeQyn{FivIx`)5|U0WbcvKT9k=O}){HKwhj@oD#i0>I;Vy*pLc-^r7TQP@Vg58id?d zLn(W?F7h)>m4!~4L{t2FL~GlnaYwi$+lEIE3~Fxl)%oB_!~ghl)?h$vgNLFk5PuOr zPbU=4v*qx7oz;NH6fAB|ua@FczrE;v=uH z_LJiW&(S1bB5!g_odhcXA(ZK_dn#IB)1teoqh?4ZXKqz0!(=(EeRX8Kt7!L0o6CDs z>%!Z9R5>0PNWek!gpB`JKnsYJ4Gavh9jx_Tb8}e3T9YHIeFEMr+ENyS=G8ewe6ykJ~h`%E|Y6sEg*(r(j3$?Q`{lW`FJ`OGD>FDZm?F^$a_n!i_7pN8?K#yQH4i1ht`Qp4W(d*1v zk?ZVvkp?3Z8mEYk$XHmHWA9d5Ch3uYF!G8)LF+3to9w&3Bgz8V>|OR}xJZ8@uS&5$AzC=ao*PirokFrTRuxP5)3 z)E;#Js}I(E`rWI+m%28y2?E}OY*=OO2eE%zLvs<*DM@gU?OYNUQ1S|>eJ?asWr|0f zFR#mt0(34}z+0wN%}XdP^^qh%+@zFLCwrZgPz9*oY1P}hd_H$4Dm6d7)2({pU0o|| z)LxU!bbGXyYW~ehdHHcF>GrCUzCW5Fz7jxDk`uF$DyCi3S(mFK;m(KO=Fq%>hDvkHYtD{!`!_kI_4R)xWG(7QkF%x}!FUnYbv0x0SBO$lYM|9*_0EUy1u;4hT?>%ae3JDBdogY>Ny!2j;% zZ@Bk=+4bMF&i~5e{{wma|3n6-X-tAaqAco7Z5;~lT`D7spPxNJmU+UsFHP1Z5n9Z! zk3pva>KGF~>kOsC{BO^opAV;;Y+los5d&q@*~`XhE{-DyxH`r)yv|BrQa}NXU7rAz=n$g>AR(vs zaAQaTFhHl`VHX#}vS!bEh*RL@oe$ST$u-)E9OgZjYHMpj)6P4MEm$xpH zNDW2N#dymiNX^V`uPjWpbEJRt5SX87^6{*fy(P{26Ci1J^sK32r8^^8AKkyoZtEh- zj9z^Ad!yj^@qAi-)}1gJPX`RRh(vZ<_Urm-bi+B}b&MvHA3bmrtvId6}G1}weWpsWAYMrdy@W}&XSPHdx6h^8Z-*t3B9i#Wp z@jVJr&@nJ-55Z&R#{=nt^&LdSHI5Dr=&C@2dX+1n5j{)`Y@6-&*LyWV@SdKYAy-V+ zY7Hj*Is_`8Pj=Yfg?{%2Ena)xbjz}#Kw~1PP#$LNF+yw!xX}q1&yaDD`xQJq1xC;| zZJzrvWTu|?XiTG07M6W3b(CF+iJ@wYohXYm1|;x7S0uR4bRMOLD+k6{{lM8JsBlLF zyslV4HvX`^7s#lv$v0=`?q)p#%Afw4IQecHp1BQ|)$$t&4BlK^4OP{xMyI3AQ0L<< zDTc2lzgO-aK_~_%|S1RL@+kJ zR^Wv_y@DJ&1qH=+9k@t^Qa`w(I$l6h>bD}8pva3fB__Fz3V;L-(Cum#j#BR#7PX_~ z&2|uO+W80wF6g|vTg1W?;YLTm-@@U+fXw(AwJyOki1`o9iuI!hzOA$g;Lupg%a1Yu z?aHpq1q9kqP_SrK*LcM;enQy3o7Jop9(TeHq-pRu9d_NA2;%vwM?c|ZpGzDqY4pg# z*qf5X7 z{v1&%n23kFG#31MrI1*2Fy!IUv%v>0hG-E=eT!dz!eN7HHTP#e?IwO6m=c5j`HFr- zH`viw36Q~cJXfkMm}2(<0Qml%^WUGwn>_0C((?0-g{sYF_Dw)_!zIjcBO!S_U7ek> zb5(^ju1st9K1Gwgj2(Hp_WZ|pu-1EY*xzcgzoT!T3j5>T!E_&EV>d>DRJ~g=($b&h zj5XZOR`xGp(48sZyXfFKC*|)>@ZU24!cs64*$=NbfFp_;VP^}(RKbh%<<3a5yVU!U z@Qf#?Ve^wto1s5qi~tJi)_{4x45EwLAwvk_c`du6Nc31r4~tWX6@ zYsWPG^0y0T_2T7U&Ik+?sbydsRhrBJ_niy0$s+=L27b1KJ^2Gb&f9`iaOhR!qwpT> zPvRPAc_wn$$KiQStNmnWJ4`f)R50WX~H$X|4Oz2l&lfBX!rY}f#?Z?UiX_AX4A<4 z;NR1)p|K(ywwpE=O;8B*F(+rkxOg1z?_)<6MsW7oW?VtPxcuK03oga(jQw0;D1Sjm zPmk@$v1b~@{QllO%l)rl^@LdYC=f#D~7|=X4{h&LRLC_r#mhH~Yn1Om`hTiX) zG(XN*lNugUCH-*oJErl==;QItz%>dkcDt?9n7bK=3bxTNBNq4CiFh10hjY)4M=>{l zjb;2XQQ%@0kfY0R%x?NGAW9duUoVJI=)~w zC&}>pb(dIrUZ-s`DzsknFP9lDP5U>E^TQc;O`o&t01UTt(RV=T=jGbbTC@|`Z!w#` zi;h;9g`Lc~fBCP@JxG-TsnM^{^|z^y`!;6?g@^3`NT#_0P-l#smX+|;%__sHV#{v_z8+|ML#}WFo1N5(}yM6%N!7xkIqCkQJ-@dLG z$~6m^;DMEBzW4C#EPx9f#+9zliqR1fH926SGmO1#aJA6tsWNOjvxQgI$sd+Eaz*-neRNaUfY6=1NVLi&!(;Wg2pEI05tZJ)oi*()->66 z=r%rwUH4j;2yHFDJOA0vxwD&_o9?-GP%UgZT;e!1+0!TA_&U&4pjvD7R!2wYt+smj zkN(TQP(mMwJ`7GcP2Zn%UhT%Y8Xq60D9kgRsuCNm+I|b$$W9jEzC2x0ASELc7?zzo zJv~KWN?gMSVt3CZv7`HoD43Y08Rb63mJ5o7OiW%*@CQzjyIf4HPkZ@7k+BpoKJq`v z6e7pS&UD83LjF1yzi6DMr^9l*kM8Px43w3??ONw$WB_z`_iwwK^1%0DBsAeYn!gMz8b?GQ!djHIz_@#wGi8x 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 Kdf{I-f&T}`-i77> literal 0 HcmV?d00001 diff --git a/docs/ref/internals/execution_model.rst b/docs/ref/internals/execution_model.rst new file mode 100644 index 00000000..15574e3a --- /dev/null +++ b/docs/ref/internals/execution_model.rst @@ -0,0 +1,22 @@ +Execution Model +=============== + +One of the many advantages of using brigade is that it will be parallelize the execution of tasks for you. The way it works is as follows: + +1. You trigger the parallelization by running a task via :obj:`brigade.core.Brigade.run` with ``num_workers > 1`` (defaults to ``20``). +2. If ``num_workers == 1`` we run the task over all hosts one after the other in a simple loop. This is useful for troubleshooting/debugging, for writing to disk/database or just for printing on screen. +3. When parallelizing tasks brigade will use a different thread for each host. + +Below you can see a simple diagram illustrating how this works: + +.. image:: _static/execution_model_1.png + +Note that you can create tasks with other tasks inside. When tasks are nested the inner tasks will run serially for that host in parallel to other hosts. This is useful as it let's you control the flow of the execution at your own will. For instance, you could compose a different workflow to the previous one as follows: + +.. image:: _static/execution_model_2.png + +Why would you do this? Most of the time you will want to group as many tasks as possible. That will ensure your script runs as fast as possible. However, some tasks might require to be run after ensuring the some others are done. For instance, you could do something like: + +1. Configure everything in parallel +2. Run some verification tests +3. Enable services diff --git a/docs/ref/internals/index.rst b/docs/ref/internals/index.rst new file mode 100644 index 00000000..4b87b948 --- /dev/null +++ b/docs/ref/internals/index.rst @@ -0,0 +1,7 @@ +Brigade's Internals +=================== + +.. toctree:: + :maxdepth: 2 + + execution_model From 10aa18488f474e8494a547f748f1784eb876b048 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Sat, 6 Jan 2018 14:40:08 +0100 Subject: [PATCH 55/67] allow passing dry_run to individual tasks (#65) --- brigade/core/__init__.py | 19 +++++++------- brigade/core/task.py | 2 +- tests/inventory_data/nsot/nsot.sqlite3 | Bin 220160 -> 228352 bytes tests/plugins/tasks/files/test_sftp.py | 24 +++++++++--------- .../tasks/networking/test_napalm_configure.py | 10 +++++--- 5 files changed, 29 insertions(+), 26 deletions(-) diff --git a/brigade/core/__init__.py b/brigade/core/__init__.py index d92400e6..2a2dfbfc 100644 --- a/brigade/core/__init__.py +++ b/brigade/core/__init__.py @@ -89,13 +89,13 @@ def filter(self, **kwargs): b.inventory = self.inventory.filter(**kwargs) return b - def _run_serial(self, task, **kwargs): + def _run_serial(self, task, dry_run, **kwargs): t = Task(task, **kwargs) result = AggregatedResult() for host in self.inventory.hosts.values(): try: logger.debug("{}: running task {}".format(host.name, t)) - r = t._start(host=host, brigade=self, dry_run=self.dry_run) + r = t._start(host=host, brigade=self, dry_run=dry_run) result[host.name] = r except Exception as e: logger.error("{}: {}".format(host, e)) @@ -103,11 +103,11 @@ def _run_serial(self, task, **kwargs): result.tracebacks[host.name] = traceback.format_exc() return result - def _run_parallel(self, task, num_workers, **kwargs): + def _run_parallel(self, task, num_workers, dry_run, **kwargs): result = AggregatedResult() pool = Pool(processes=num_workers) - result_pool = [pool.apply_async(run_task, args=(h, self, Task(task, **kwargs))) + result_pool = [pool.apply_async(run_task, args=(h, self, dry_run, Task(task, **kwargs))) for h in self.inventory.hosts.values()] pool.close() pool.join() @@ -121,7 +121,7 @@ def _run_parallel(self, task, num_workers, **kwargs): result[host] = res return result - def run(self, task, num_workers=None, **kwargs): + def run(self, task, num_workers=None, dry_run=None, **kwargs): """ Run task over all the hosts in the inventory. @@ -129,6 +129,7 @@ def run(self, task, num_workers=None, **kwargs): task (``callable``): function or callable that will be run against each device in the inventory num_workers(``int``): Override for how many hosts to run in paralell for this task + dry_run(``bool``): Whether if we are testing the changes or not **kwargs: additional argument to pass to ``task`` when calling it Raises: @@ -141,19 +142,19 @@ def run(self, task, num_workers=None, **kwargs): num_workers = num_workers or self.config.num_workers if num_workers == 1: - result = self._run_serial(task, **kwargs) + result = self._run_serial(task, dry_run, **kwargs) else: - result = self._run_parallel(task, num_workers, **kwargs) + result = self._run_parallel(task, num_workers, dry_run, **kwargs) if self.config.raise_on_error: result.raise_on_error() return result -def run_task(host, brigade, task): +def run_task(host, brigade, dry_run, task): try: logger.debug("{}: running task {}".format(host.name, task)) - r = task._start(host=host, brigade=brigade, dry_run=brigade.dry_run) + r = task._start(host=host, brigade=brigade, dry_run=dry_run) return host.name, r, None, None except Exception as e: logger.error("{}: {}".format(host, e)) diff --git a/brigade/core/task.py b/brigade/core/task.py index eee86b06..72a1e694 100644 --- a/brigade/core/task.py +++ b/brigade/core/task.py @@ -35,7 +35,7 @@ def __repr__(self): def _start(self, host, brigade, dry_run): self.host = host self.brigade = brigade - self.dry_run = dry_run + self.dry_run = dry_run if dry_run is not None else brigade.dry_run return self.task(self, **self.params) def run(self, task, **kwargs): diff --git a/tests/inventory_data/nsot/nsot.sqlite3 b/tests/inventory_data/nsot/nsot.sqlite3 index a1217f69ee7103d2e7c960baf0f341ea4340190d..5fd8479baee4161f562baafe6f0233a43985d474 100644 GIT binary patch delta 3206 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 cX5QaOMZ6p!rx9 Date: Sat, 6 Jan 2018 18:23:07 +0100 Subject: [PATCH 56/67] skip hosts if they fail and raise_on_error is False --- brigade/core/__init__.py | 20 ++++++++++- brigade/core/task.py | 31 ++++++++++++++--- .../tasks/networking/napalm_validate.py | 5 ++- tests/core/test_tasks.py | 34 +++++++++++++++++++ .../tasks/networking/test_napalm_validate.py | 5 +-- 5 files changed, 84 insertions(+), 11 deletions(-) diff --git a/brigade/core/__init__.py b/brigade/core/__init__.py index 1f24293b..fcb67d95 100644 --- a/brigade/core/__init__.py +++ b/brigade/core/__init__.py @@ -34,6 +34,19 @@ def _unpickle_method(func_name, obj, cls): copy_reg.pickle(types.MethodType, _pickle_method, _unpickle_method) +class Data(object): + """ + This class is just a placeholder to share data amongsts different + versions of Brigade after running ``filter`` multiple times. + + Attributes: + failed_hosts (list): Hosts that have failed to run a task properly + """ + + def __init__(self): + self.failed_hosts = set() + + class Brigade(object): """ This is the main object to work with. It contains the inventory and it serves @@ -41,6 +54,7 @@ class Brigade(object): Arguments: inventory (:obj:`brigade.core.inventory.Inventory`): Inventory to work with + data(:obj:`brigade.core.Data`): shared data amongst different iterations of brigade dry_run(``bool``): Whether if we are testing the changes or not config (:obj:`brigade.core.configuration.Config`): Configuration object config_file (``str``): Path to Yaml configuration file @@ -49,15 +63,17 @@ class Brigade(object): Attributes: inventory (:obj:`brigade.core.inventory.Inventory`): Inventory to work with + data(:obj:`brigade.core.Data`): shared data amongst different iterations of brigade dry_run(``bool``): Whether if we are testing the changes or not config (:obj:`brigade.core.configuration.Config`): Configuration parameters available_connections (``dict``): dict of connection types are available """ def __init__(self, inventory, dry_run, config=None, config_file=None, - available_connections=None, logger=None): + available_connections=None, logger=None, data=None): self.logger = logger or logging.getLogger("brigade") + self.data = data or Data() self.inventory = inventory self.inventory.brigade = self @@ -173,6 +189,8 @@ def run(self, task, num_workers=None, dry_run=None, raise_on_error=None, **kwarg self.config.raise_on_error if raise_on_error: result.raise_on_error() + else: + self.data.failed_hosts.update(result.failed_hosts.keys()) return result diff --git a/brigade/core/task.py b/brigade/core/task.py index cdff5290..a65a0343 100644 --- a/brigade/core/task.py +++ b/brigade/core/task.py @@ -32,10 +32,13 @@ def __repr__(self): return self.name def _start(self, host, brigade, dry_run, sub_task=False): - self.host = host - self.brigade = brigade - self.dry_run = dry_run if dry_run is not None else brigade.dry_run - r = self.task(self, **self.params) or Result(host) + if host.name in brigade.data.failed_hosts: + r = Result(host, skipped=True) + else: + self.host = host + self.brigade = brigade + self.dry_run = dry_run if dry_run is not None else brigade.dry_run + r = self.task(self, **self.params) or Result(host) r.name = self.name if sub_task: @@ -80,6 +83,7 @@ class Result(object): host (:obj:`brigade.core.inventory.Host`): Reference to the host that lead ot this result failed (bool): Whether the execution failed or not exception (Exception): uncaught exception thrown during the exection of the task (if any) + skipped (bool): ``True`` if the host was skipped Attributes: changed (bool): ``True`` if the task is changing the system @@ -88,16 +92,18 @@ class Result(object): host (:obj:`brigade.core.inventory.Host`): Reference to the host that lead ot this result failed (bool): Whether the execution failed or not exception (Exception): uncaught exception thrown during the exection of the task (if any) + skipped (bool): ``True`` if the host was skipped """ def __init__(self, host, result=None, changed=False, diff="", failed=False, exception=None, - **kwargs): + skipped=False, **kwargs): self.result = result self.host = host self.changed = changed self.diff = diff self.failed = failed self.exception = exception + self.skipped = skipped for k, v in kwargs.items(): setattr(self, k, v) @@ -120,6 +126,16 @@ def failed(self): """If ``True`` at least a host failed.""" return any([h.failed for h in self.values()]) + @property + def failed_hosts(self): + """Hosts that failed during the execution of the task.""" + return {h: r for h, r in self.items() if r.failed} + + @property + def skipped(self): + """If ``True`` at least a host was skipped.""" + return any([h.skipped for h in self.values()]) + def raise_on_error(self): """ Raises: @@ -145,6 +161,11 @@ def failed(self): """If ``True`` at least a task failed.""" return any([h.failed for h in self]) + @property + def skipped(self): + """If ``True`` at least a host was skipped.""" + return any([h.skipped for h in self]) + @property def changed(self): """If ``True`` at least a task changed the system.""" diff --git a/brigade/plugins/tasks/networking/napalm_validate.py b/brigade/plugins/tasks/networking/napalm_validate.py index 76e9ab9d..e156b9e8 100644 --- a/brigade/plugins/tasks/networking/napalm_validate.py +++ b/brigade/plugins/tasks/networking/napalm_validate.py @@ -14,9 +14,8 @@ def napalm_validate(task, src=None, validation_source=None): Returns: :obj:`brigade.core.task.Result`: * result (``dict``): dictionary with the result of the validation - * failed (``bool``): Whether the device complies or not (note this will trigger a - :obj:`brigade.core.exceptions.BrigadeExecutionError` if raise_on_error is set to ``True``) + * complies (``bool``): Whether the device complies or not """ device = task.host.get_connection("napalm") r = device.compliance_report(validation_file=src, validation_source=validation_source) - return Result(host=task.host, result=r, failed=not r["complies"]) + return Result(host=task.host, result=r) diff --git a/tests/core/test_tasks.py b/tests/core/test_tasks.py index 8487ca06..a3ac2ede 100644 --- a/tests/core/test_tasks.py +++ b/tests/core/test_tasks.py @@ -1,6 +1,16 @@ from brigade.plugins.tasks import commands +def task_fails_for_some(task): + if task.host.name == "dev3.group_2": + # let's hardcode a failure + task.run(commands.command, + command="sasdasdasd") + else: + task.run(commands.command, + command="echo {}".format(task.host)) + + def sub_task(task): task.run(commands.command, command="echo {}".format(task.host)) @@ -22,3 +32,27 @@ def test_sub_task(self, brigade): assert r[0].name == "sub_task" assert r[1].name == "command" assert h == r[1].stdout.strip() + + def test_skip_failed_host(self, brigade): + result = brigade.run(task_fails_for_some, raise_on_error=False) + assert result.failed + assert not result.skipped + for h, r in result.items(): + if h == "dev3.group_2": + assert r.failed + else: + assert not r.failed + assert h == r[1].stdout.strip() + + result = brigade.run(task_fails_for_some) + assert not result.failed + assert result.skipped + for h, r in result.items(): + if h == "dev3.group_2": + assert r.skipped + else: + assert not r.skipped + assert h == r[1].stdout.strip() + + # let's reset it + brigade.data.failed_hosts = set() diff --git a/tests/plugins/tasks/networking/test_napalm_validate.py b/tests/plugins/tasks/networking/test_napalm_validate.py index f2c1c159..5a6c3b0a 100644 --- a/tests/plugins/tasks/networking/test_napalm_validate.py +++ b/tests/plugins/tasks/networking/test_napalm_validate.py @@ -24,12 +24,12 @@ def test_napalm_validate_src_error(self, brigade): print(opt["path"]) d = brigade.filter(name="dev3.group_2") d.run(connections.napalm_connection, optional_args=opt) + result = d.run(networking.napalm_validate, - raise_on_error=False, src=THIS_DIR + "/data/validate_error.yaml") assert result for h, r in result.items(): - assert r.failed + assert not r.failed assert not r.result["complies"] def test_napalm_validate_src_validate_source(self, brigade): @@ -48,3 +48,4 @@ def test_napalm_validate_src_validate_source(self, brigade): assert result for h, r in result.items(): assert not r.failed + assert r.result["complies"] From 8b8a4d681af5c5c7e37115a56291e63463496290 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Sat, 6 Jan 2018 20:01:52 +0100 Subject: [PATCH 57/67] added easy_brigade --- brigade/easy.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 brigade/easy.py diff --git a/brigade/easy.py b/brigade/easy.py new file mode 100644 index 00000000..f05ba6aa --- /dev/null +++ b/brigade/easy.py @@ -0,0 +1,23 @@ +from brigade.core import Brigade +from brigade.core.configuration import Config +from brigade.plugins.inventory.simple import SimpleInventory + + +def easy_brigade(host_file="host.yaml", group_file="groups.yaml", dry_run=True, **kwargs): + """ + Helper function to create easily a :obj:`brigade.core.Brigade` object. + + Arguments: + host_file (str): path to the host file that will be passed to + :obj:`brigade.plugins.inventory.simple.SimpleInventory` + group_file (str): path to the group file that will be passed to + :obj:`brigade.plugins.inventory.simple.SimpleInventory` + dry_run (bool): whether if this is a dry run or not + **kwargs: Configuration parameters, see + :doc:`configuration parameters ` + """ + return Brigade( + inventory=SimpleInventory(host_file, group_file), + dry_run=dry_run, + config=Config(**kwargs), + ) From a7096c65f30409676df56ae4c5be3361fca75d30 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Sat, 6 Jan 2018 20:02:50 +0100 Subject: [PATCH 58/67] added tutorials --- docs/howto/basic-napalm-getters.rst | 4 +- docs/ref/api/brigade.rst | 7 + docs/ref/api/easy.rst | 6 + docs/ref/api/index.rst | 3 +- docs/ref/api/task.rst | 7 + docs/ref/functions/index.rst | 8 + docs/ref/functions/text.rst | 6 + docs/ref/index.rst | 1 + docs/ref/inventory/index.rst | 2 + docs/tutorials/intro/brigade.rst | 74 ++++ docs/tutorials/intro/explore.rst | 2 - docs/tutorials/intro/index.rst | 9 +- docs/tutorials/intro/inventory.rst | 133 +++++- docs/tutorials/intro/run.rst | 2 - docs/tutorials/intro/running_tasks.rst | 89 ++++ .../intro/running_tasks_different_hosts.rst | 72 ++++ docs/tutorials/intro/running_tasks_errors.rst | 390 ++++++++++++++++++ .../intro/running_tasks_grouping.rst | 227 ++++++++++ examples/1_simple_runbooks/get_facts.py | 26 +- examples/{ => inventory}/Vagrantfile | 0 examples/{ => inventory}/groups.yaml | 0 examples/{ => inventory}/hosts.yaml | 0 .../{ => inventory}/network_diagram.graffle | Bin examples/{ => inventory}/network_diagram.png | Bin 24 files changed, 1045 insertions(+), 23 deletions(-) create mode 100644 docs/ref/api/easy.rst create mode 100644 docs/ref/functions/index.rst create mode 100644 docs/ref/functions/text.rst create mode 100644 docs/tutorials/intro/brigade.rst delete mode 100644 docs/tutorials/intro/explore.rst delete mode 100644 docs/tutorials/intro/run.rst create mode 100644 docs/tutorials/intro/running_tasks.rst create mode 100644 docs/tutorials/intro/running_tasks_different_hosts.rst create mode 100644 docs/tutorials/intro/running_tasks_errors.rst create mode 100644 docs/tutorials/intro/running_tasks_grouping.rst rename examples/{ => inventory}/Vagrantfile (100%) rename examples/{ => inventory}/groups.yaml (100%) rename examples/{ => inventory}/hosts.yaml (100%) rename examples/{ => inventory}/network_diagram.graffle (100%) rename examples/{ => inventory}/network_diagram.png (100%) diff --git a/docs/howto/basic-napalm-getters.rst b/docs/howto/basic-napalm-getters.rst index d72e0c6f..dbd65e6e 100644 --- a/docs/howto/basic-napalm-getters.rst +++ b/docs/howto/basic-napalm-getters.rst @@ -8,13 +8,13 @@ Let's start by seeing how to work with the inventory. Let's assume the following * Hosts file: -.. literalinclude:: ../../examples/hosts.yaml +.. literalinclude:: ../../examples/inventory/hosts.yaml :name: hosts.yaml :language: yaml * Groups file: -.. literalinclude:: ../../examples/groups.yaml +.. literalinclude:: ../../examples/inventory/groups.yaml :name: groups.yaml :language: yaml diff --git a/docs/ref/api/brigade.rst b/docs/ref/api/brigade.rst index 94fdc833..0ea4f024 100644 --- a/docs/ref/api/brigade.rst +++ b/docs/ref/api/brigade.rst @@ -1,3 +1,10 @@ +Data +#### + +.. autoclass:: brigade.core.Data + :members: + :undoc-members: + Brigade ####### diff --git a/docs/ref/api/easy.rst b/docs/ref/api/easy.rst new file mode 100644 index 00000000..02de78a5 --- /dev/null +++ b/docs/ref/api/easy.rst @@ -0,0 +1,6 @@ +Easy +==== + +.. automodule:: brigade.easy + :members: + :undoc-members: diff --git a/docs/ref/api/index.rst b/docs/ref/api/index.rst index a53ebe21..7ca6e828 100644 --- a/docs/ref/api/index.rst +++ b/docs/ref/api/index.rst @@ -8,5 +8,6 @@ Brigade API Reference brigade configuration inventory + easy task - exceptions \ No newline at end of file + exceptions diff --git a/docs/ref/api/task.rst b/docs/ref/api/task.rst index 2a86f698..8ccf2b8e 100644 --- a/docs/ref/api/task.rst +++ b/docs/ref/api/task.rst @@ -18,3 +18,10 @@ AggregatedResult .. autoclass:: brigade.core.task.AggregatedResult :members: :undoc-members: + +MultiResult +################ + +.. autoclass:: brigade.core.task.MultiResult + :members: + :undoc-members: diff --git a/docs/ref/functions/index.rst b/docs/ref/functions/index.rst new file mode 100644 index 00000000..cf781f1d --- /dev/null +++ b/docs/ref/functions/index.rst @@ -0,0 +1,8 @@ +Functions +========= + +.. toctree:: + :maxdepth: 2 + :glob: + + * diff --git a/docs/ref/functions/text.rst b/docs/ref/functions/text.rst new file mode 100644 index 00000000..6f62d224 --- /dev/null +++ b/docs/ref/functions/text.rst @@ -0,0 +1,6 @@ +Text +==== + +.. automodule:: brigade.plugins.functions.text + :members: + :undoc-members: diff --git a/docs/ref/index.rst b/docs/ref/index.rst index 7b3d72bb..9e863098 100644 --- a/docs/ref/index.rst +++ b/docs/ref/index.rst @@ -24,4 +24,5 @@ Reference Guides :caption: Plugins tasks/index + functions/index inventory/index diff --git a/docs/ref/inventory/index.rst b/docs/ref/inventory/index.rst index 5af16f3f..cf659aea 100644 --- a/docs/ref/inventory/index.rst +++ b/docs/ref/inventory/index.rst @@ -1,3 +1,5 @@ +.. _ref-inventory: + Inventory ========= diff --git a/docs/tutorials/intro/brigade.rst b/docs/tutorials/intro/brigade.rst new file mode 100644 index 00000000..ae5f7c10 --- /dev/null +++ b/docs/tutorials/intro/brigade.rst @@ -0,0 +1,74 @@ +Brigade +======= + +Now that we know how the inventory works let's create a brigade object we can start working with. There are two ways we can use: + +1. Using the :obj:`brigade.core.Brigade` directly, which is quite simple and the most flexible and versatile option. +2. Using :obj:`brigade.easy.easy_brigade`, which is simpler and good enough for most cases. + +Using the "raw" API +------------------- + +If you want to use the "raw" API you need two things: + +1. A configuration object. +2. An inventory object. + +Once you have them, you can create the brigade object yourself. For example:: + + >>> from brigade.core import Brigade + >>> from brigade.core.configuration import Config + >>> from brigade.plugins.inventory.simple import SimpleInventory + >>> + >>> brigade = Brigade( + ... inventory=SimpleInventory("hosts.yaml", "groups.yaml"), + ... dry_run=False, + ... config=Config(raise_on_error=False), + ... ) + >>> + +Using ``easy_brigade`` +---------------------- + +With :obj:`brigade.easy.easy_brigade` you only need to do:: + + >>> from brigade.easy import easy_brigade + >>> brigade = easy_brigade( + ... host_file="hosts.yaml", group_file="groups.yaml", + ... dry_run=True, + ... raise_on_error=False, + ... ) + >>> + +As you can see is not that different from above but you save a few imports. + +Brigade's Inventory +------------------- + +Brigade's object will always have a reference to the inventory you can inspect and work with if you have the need. For instance:: + + >>> brigade.inventory + + >>> brigade.inventory.hosts + {'host1.cmh': Host: host1.cmh, 'host2.cmh': Host: host2.cmh, 'spine00.cmh': Host: spine00.cmh, 'spine01.cmh': Host: spine01.cmh, 'leaf00.cmh': Host: leaf00.cmh, 'leaf01.cmh': Host: leaf01.cmh, 'host1.bma': Host: host1.bma, 'host2.bma': Host: host2.bma, 'spine00.bma': Host: spine00.bma, 'spine01.bma': Host: spine01.bma, 'leaf00.bma': Host: leaf00.bma, 'leaf01.bma': Host: leaf01.bma} + >>> brigade.inventory.groups + {'all': Group: all, 'bma': Group: bma, 'cmh': Group: cmh} + +As you will see further on in the tutorial you will rarely need to work with the inventory yourself as brigade will take care of it for you automatically but it's always good to know you have it there if you need to. + +Filtering the hosts +___________________ + +As we could see in the :doc:`Inventory ` section we could filter hosts based on data and attributes. The brigade object can leverage on that feature to "replicate" itself with subsets of devices allowing you to group your devices and perform actions on them as you see fit:: + + >>> switches = brigade.filter(type="network_device") + >>> switches.inventory.hosts + {'spine00.cmh': Host: spine00.cmh, 'spine01.cmh': Host: spine01.cmh, 'leaf00.cmh': Host: leaf00.cmh, 'leaf01.cmh': Host: leaf01.cmh, 'spine00.bma': Host: spine00.bma, 'spine01.bma': Host: spine01.bma, 'leaf00.bma': Host: leaf00.bma, 'leaf01.bma': Host: leaf01.bma} + >>> switches_in_bma = switches.filter(site="bma") + >>> switches_in_bma.inventory.hosts + {'spine00.bma': Host: spine00.bma, 'spine01.bma': Host: spine01.bma, 'leaf00.bma': Host: leaf00.bma, 'leaf01.bma': Host: leaf01.bma} + >>> hosts = brigade.filter(type="host") + >>> hosts.inventory.hosts + {'host1.cmh': Host: host1.cmh, 'host2.cmh': Host: host2.cmh, 'host1.bma': Host: host1.bma, 'host2.bma': Host: host2.bma} + +All of the "replicas" of brigade will contain the same data and configuration, only the hosts will differ. diff --git a/docs/tutorials/intro/explore.rst b/docs/tutorials/intro/explore.rst deleted file mode 100644 index 08bda4be..00000000 --- a/docs/tutorials/intro/explore.rst +++ /dev/null @@ -1,2 +0,0 @@ -Exploring the inventory in Brigade -================================== diff --git a/docs/tutorials/intro/index.rst b/docs/tutorials/intro/index.rst index 68737c6c..850a458e 100644 --- a/docs/tutorials/intro/index.rst +++ b/docs/tutorials/intro/index.rst @@ -13,6 +13,9 @@ We're glad you made it here! This is a great place to learn the basics of Brigad Brigade at a glance 100% Python Installation guide - Creating an inventory - Exploring the inventory - Running tasks \ No newline at end of file + inventory + brigade + running_tasks + running_tasks_different_hosts + running_tasks_grouping + running_tasks_errors diff --git a/docs/tutorials/intro/inventory.rst b/docs/tutorials/intro/inventory.rst index 9a019474..5cd1b481 100644 --- a/docs/tutorials/intro/inventory.rst +++ b/docs/tutorials/intro/inventory.rst @@ -1,2 +1,131 @@ -Creating an inventory for Brigade -================================= +The Inventory +============= + +The inventory is arguably the most important piece of Brigade. The inventory organizes hosts and makes sure tasks have the correct data for each host. + +You can create the inventory in different ways, depending on your data source. To see the available plugins you can use go to the :ref:`ref-inventory` reference guide. + +.. note:: For this and the subsequent sections of this tutorial we are going to use the :obj:`SimpleInventory ` with the data located in ``/examples/inventory/``. We will also use the ``Vagrantfile`` located there so you should be able to reproduce everything. You can head to `Hosts/Groups contents`_ to see the contents of the file just for reference. + +First, let's create the inventory:: + + >>> from brigade.plugins.inventory.simple import SimpleInventory + >>> inventory = SimpleInventory(host_file="hosts.yaml", group_file="groups.yaml") + +Now let's inspect the hosts and groups we have:: + + >>> inventory.hosts + {'host1.cmh': Host: host1.cmh, 'host2.cmh': Host: host2.cmh, 'spine00.cmh': Host: spine00.cmh, 'spine01.cmh': Host: spine01.cmh, 'leaf00.cmh': Host: leaf00.cmh, 'leaf01.cmh': Host: leaf01.cmh, 'host1.bma': Host: host1.bma, 'host2.bma': Host: host2.bma, 'spine00.bma': Host: spine00.bma, 'spine01.bma': Host: spine01.bma, 'leaf00.bma': Host: leaf00.bma, 'leaf01.bma': Host: leaf01.bma} + >>> inventory.groups + {'all': Group: all, 'bma': Group: bma, 'cmh': Group: cmh} + +As you probably noticed both ``hosts`` and ``groups`` are dictionaries so you can iterate over them if you want to. + +Data +---- + +Let's start by grabbing a host: + + >>> h = inventory.hosts['host1.cmh'] + >>> print(h) + host1.cmh + +Now, let's check some attributes:: + + >>> h["site"] + 'cmh' + >>> h.data["role"] + 'host' + >>> h["domain"] + 'acme.com' + >>> h.data["domain"] + Traceback (most recent call last): + File "", line 1, in + KeyError: 'domain' + >>> h.group["domain"] + 'acme.com' + +What does this mean? You can access host data in two ways: + +1. As if the host was a dictionary, i.e., ``h["domain"]`` in which case the inventory will resolve the groups and use data inherited from them (in our example ``domain`` is coming from the parent group). +2. Via the ``data`` attribute in which case there is no group resolution going on so ``h["domain"]`` fails is that piece of data is not directly assigned to the host. + +Most of the time you will care about the first option but if you ever need to get data only from the host you can do it without a hassle. + +Finally, the host behaves like a python dictionary so you can iterate over the data as such:: + + >>> h.keys() + dict_keys(['name', 'group', 'asn', 'vlans', 'site', 'role', 'brigade_nos', 'type']) + >>> h.values() + dict_values(['host1.cmh', 'cmh', 65000, {100: 'frontend', 200: 'backend'}, 'cmh', 'host', 'linux', 'host']) + >>> h.items() + dict_items([('name', 'host1.cmh'), ('group', 'cmh'), ('asn', 65000), ('vlans', {100: 'frontend', 200: 'backend'}), ('site', 'cmh'), ('role', 'host'), ('brigade_nos', 'linux'), ('type', 'host')]) + >>> for k, v in h.items(): + ... print(k, v) + ... + name host1.cmh + group cmh + asn 65000 + vlans {100: 'frontend', 200: 'backend'} + site cmh + role host + brigade_nos linux + type host + >>> + +.. note:: You can head to :obj:`brigade.core.inventory.Host` and :obj:`brigade.core.inventory.Group` for details on all the available attributes and functions for each ``host`` and ``group``. + +Filtering the inventory +----------------------- + +You won't always want to operate over all hosts, sometimes you will want to operate over some of them based on some attributes. In order to do so the inventory can help you filtering based on it's attributes. For instance:: + + >>> inventory.hosts.keys() + dict_keys(['host1.cmh', 'host2.cmh', 'spine00.cmh', 'spine01.cmh', 'leaf00.cmh', 'leaf01.cmh', 'host1.bma', 'host2.bma', 'spine00.bma', 'spine01.bma', 'leaf00.bma', 'leaf01.bma']) + >>> inventory.filter(site="bma").hosts.keys() + dict_keys(['host1.bma', 'host2.bma', 'spine00.bma', 'spine01.bma', 'leaf00.bma', 'leaf01.bma']) + >>> inventory.filter(site="bma", role="spine").hosts.keys() + dict_keys(['spine00.bma', 'spine01.bma']) + >>> inventory.filter(site="bma").filter(role="spine").hosts.keys() + dict_keys(['spine00.bma', 'spine01.bma']) + +Note in the last line that the filter is cumulative so you can do things like this: + + >>> cmh = inventory.filter(site="cmh") + >>> cmh.hosts.keys() + dict_keys(['host1.cmh', 'host2.cmh', 'spine00.cmh', 'spine01.cmh', 'leaf00.cmh', 'leaf01.cmh']) + >>> cmh_eos = cmh.filter(brigade_nos="eos") + >>> cmh_eos.hosts.keys() + dict_keys(['spine00.cmh', 'leaf00.cmh']) + >>> cmh_eos.filter(role="spine").hosts.keys() + dict_keys(['spine00.cmh']) + +This should give you enough room to build groups in any way you want. + +Advanced filtering +__________________ + +You can also do more complex filtering by using functions or lambdas:: + + >>> def has_long_name(host): + ... return len(host.name) == 11 + ... + >>> inventory.filter(filter_func=has_long_name).hosts.keys() + dict_keys(['spine00.cmh', 'spine01.cmh', 'spine00.bma', 'spine01.bma']) + >>> inventory.filter(filter_func=lambda h: len(h.name) == 9).hosts.keys() + dict_keys(['host1.cmh', 'host2.cmh', 'host1.bma', 'host2.bma']) + +Not the most useful example but it should be enough to illustrate how it works. + + +Hosts/Groups contents +--------------------- + + +* ``hosts.yaml`` + +.. literalinclude:: ../../../examples/inventory/hosts.yaml + +* ``groups.yaml`` + +.. literalinclude:: ../../../examples/inventory/groups.yaml diff --git a/docs/tutorials/intro/run.rst b/docs/tutorials/intro/run.rst deleted file mode 100644 index a09cc28b..00000000 --- a/docs/tutorials/intro/run.rst +++ /dev/null @@ -1,2 +0,0 @@ -Running tasks with Brigade -========================== diff --git a/docs/tutorials/intro/running_tasks.rst b/docs/tutorials/intro/running_tasks.rst new file mode 100644 index 00000000..63edcbf9 --- /dev/null +++ b/docs/tutorials/intro/running_tasks.rst @@ -0,0 +1,89 @@ +Running tasks +============= + +Once you have your brigade objects you can start running :doc:`tasks `. The first thing you have to do is import the task you want to use:: + + >>> from brigade.plugins.tasks.commands import command + +Now you should be able to run that task for all devices:: + + >>> result = brigade.run(command, + ... command="echo hi! I am {host} and I am a {host.nos} device") + +.. note:: Note you can format strings using host data. + +This should give us a :obj:`brigade.core.task.AggregatedResult` object, which is a dictionary-like object where the key is the name of ``Host`` and the value a :obj:`brigade.core.task.Result`. + +Now, we can iterate over the object:: + + >>> for host, res in result.items(): + ... print(host + ": " + res.stdout) + ... + host1.cmh: hi! I am host1.cmh and I am a linux device + host2.cmh: hi! I am host2.cmh and I am a linux device + spine00.cmh: hi! I am spine00.cmh and I am a eos device + spine01.cmh: hi! I am spine01.cmh and I am a junos device + leaf00.cmh: hi! I am leaf00.cmh and I am a eos device + leaf01.cmh: hi! I am leaf01.cmh and I am a junos device + host1.bma: hi! I am host1.bma and I am a linux device + host2.bma: hi! I am host2.bma and I am a linux device + spine00.bma: hi! I am spine00.bma and I am a eos device + spine01.bma: hi! I am spine01.bma and I am a junos device + leaf00.bma: hi! I am leaf00.bma and I am a eos device + leaf01.bma: hi! I am leaf01.bma and I am a junos device + +Or we can use a task that knows how to operate on the :obj:`brigade.core.task.AggregatedResult` object like the task :obj:`brigade.plugins.tasks.text.print_result`:: + + >>> b.run(print_result, + ... num_workers=1, + ... data=result, + ... vars=["stdout"]) + * host1.cmh ** changed : False ************************************************* + ---- command ** changed : False ----------------------------------------------- + hi! I am host1.cmh and I am a linux device + + * host2.cmh ** changed : False ************************************************* + ---- command ** changed : False ----------------------------------------------- + hi! I am host2.cmh and I am a linux device + + * spine00.cmh ** changed : False *********************************************** + ---- command ** changed : False ----------------------------------------------- + hi! I am spine00.cmh and I am a eos device + + * spine01.cmh ** changed : False *********************************************** + ---- command ** changed : False ----------------------------------------------- + hi! I am spine01.cmh and I am a junos device + + * leaf00.cmh ** changed : False ************************************************ + ---- command ** changed : False ----------------------------------------------- + hi! I am leaf00.cmh and I am a eos device + + * leaf01.cmh ** changed : False ************************************************ + ---- command ** changed : False ----------------------------------------------- + hi! I am leaf01.cmh and I am a junos device + + * host1.bma ** changed : False ************************************************* + ---- command ** changed : False ----------------------------------------------- + hi! I am host1.bma and I am a linux device + + * host2.bma ** changed : False ************************************************* + ---- command ** changed : False ----------------------------------------------- + hi! I am host2.bma and I am a linux device + + * spine00.bma ** changed : False *********************************************** + ---- command ** changed : False ----------------------------------------------- + hi! I am spine00.bma and I am a eos device + + * spine01.bma ** changed : False *********************************************** + ---- command ** changed : False ----------------------------------------------- + hi! I am spine01.bma and I am a junos device + + * leaf00.bma ** changed : False ************************************************ + ---- command ** changed : False ----------------------------------------------- + hi! I am leaf00.bma and I am a eos device + + * leaf01.bma ** changed : False ************************************************ + ---- command ** changed : False ----------------------------------------------- + hi! I am leaf01.bma and I am a junos device + +.. note:: We need to pass ``num_workers=1`` to the ``print_result`` task because otherwise brigade will run each host at the same time using multithreading mangling the output. diff --git a/docs/tutorials/intro/running_tasks_different_hosts.rst b/docs/tutorials/intro/running_tasks_different_hosts.rst new file mode 100644 index 00000000..864d5b00 --- /dev/null +++ b/docs/tutorials/intro/running_tasks_different_hosts.rst @@ -0,0 +1,72 @@ +Running tasks on different groups of hosts +========================================== + +Below you can see an example where we use the ``filtering`` capabilities of ``brigade`` to run different tasks on different devices:: + + >>> switches = brigade.filter(type="network_device") + >>> hosts = brigade.filter(type="host") + >>> + >>> rs = switches.run(command, + ... command="echo I am a switch") + >>> + >>> rh = hosts.run(command, + ... command="echo I am a host") + +Because :obj:`brigade.core.task.AggregatedResult` objects behave like dictionaries you can add the results of the second task to the result of the first one:: + + >>> rs.update(rh) + +And then just print the result for all the devices:: + + >>> brigade.run(print_result, + ... num_workers=1, + ... data=rs, + ... vars=["stdout"]) + * host1.cmh ** changed : False ************************************************* + ---- command ** changed : False ----------------------------------------------- + I am a host + + * host2.cmh ** changed : False ************************************************* + ---- command ** changed : False ----------------------------------------------- + I am a host + + * spine00.cmh ** changed : False *********************************************** + ---- command ** changed : False ----------------------------------------------- + I am a switch + + * spine01.cmh ** changed : False *********************************************** + ---- command ** changed : False ----------------------------------------------- + I am a switch + + * leaf00.cmh ** changed : False ************************************************ + ---- command ** changed : False ----------------------------------------------- + I am a switch + + * leaf01.cmh ** changed : False ************************************************ + ---- command ** changed : False ----------------------------------------------- + I am a switch + + * host1.bma ** changed : False ************************************************* + ---- command ** changed : False ----------------------------------------------- + I am a host + + * host2.bma ** changed : False ************************************************* + ---- command ** changed : False ----------------------------------------------- + I am a host + + * spine00.bma ** changed : False *********************************************** + ---- command ** changed : False ----------------------------------------------- + I am a switch + + * spine01.bma ** changed : False *********************************************** + ---- command ** changed : False ----------------------------------------------- + I am a switch + + * leaf00.bma ** changed : False ************************************************ + ---- command ** changed : False ----------------------------------------------- + I am a switch + + * leaf01.bma ** changed : False ************************************************ + ---- command ** changed : False ----------------------------------------------- + I am a switch + diff --git a/docs/tutorials/intro/running_tasks_errors.rst b/docs/tutorials/intro/running_tasks_errors.rst new file mode 100644 index 00000000..1ef3d1ac --- /dev/null +++ b/docs/tutorials/intro/running_tasks_errors.rst @@ -0,0 +1,390 @@ +Dealing with task errors +======================== + +Tasks can fail due to many reasons. A continuation we will see how to deal with errors effectively with brigade. + +Failing by default +------------------ + +Brigade can raise a :obj:`brigade.core.exceptions.BrigadeExecutionError` exception automatically as soon as an error occurs. For instance:: + + >>> brigade = easy_brigade( + ... host_file="hosts.yaml", group_file="groups.yaml", + ... dry_run=True, + ... raise_on_error=True, + ... ) + >>> + >>> + >>> def task_that_sometimes_fails(task): + ... if task.host.name == "leaf00.cmh": + ... raise Exception("an uncontrolled exception happened") + ... elif task.host.name == "leaf01.cmh": + ... return Result(host=task.host, result="yikes", failed=True) + ... else: + ... return Result(host=task.host, result="swoosh") + ... + >>> + >>> b = brigade.filter(site="cmh") + >>> r = b.run(task_that_sometimes_fails) + Traceback (most recent call last): + File "", line 1, in + File "/Users/dbarroso/workspace/brigade/brigade/core/__init__.py", line 191, in run + result.raise_on_error() + File "/Users/dbarroso/workspace/brigade/brigade/core/task.py", line 145, in raise_on_error + raise BrigadeExecutionError(self) + brigade.core.exceptions.BrigadeExecutionError: + ######################################## + # host1.cmh (succeeded) + ######################################## + swoosh + ######################################## + # host2.cmh (succeeded) + ######################################## + swoosh + ######################################## + # spine00.cmh (succeeded) + ######################################## + swoosh + ######################################## + # spine01.cmh (succeeded) + ######################################## + swoosh + ######################################## + # leaf00.cmh (failed) + ######################################## + Traceback (most recent call last): + File "/Users/dbarroso/workspace/brigade/brigade/core/__init__.py", line 201, in run_task + r = task._start(host=host, brigade=brigade, dry_run=dry_run) + File "/Users/dbarroso/workspace/brigade/brigade/core/task.py", line 41, in _start + r = self.task(self, **self.params) or Result(host) + File "", line 3, in task_that_sometimes_fails + Exception: an uncontrolled exception happened + + ######################################## + # leaf01.cmh (failed) + ######################################## + yikes + +Ok, let's see what happened there. First, we configured the default behavior to raise an Exception as soon as an error occurs:: + + >>> brigade = easy_brigade( + ... host_file="hosts.yaml", group_file="groups.yaml", + ... dry_run=True, + ... raise_on_error=True, + ... ) + >>> + +Then, the following task fails with an exception for ``leaf00.cmh`` and with a controlled error on ``leaf01.cmh``. It doesn't matter if the error is controlled or not, both cases will trigger brigade to raise an Exception. + + >>> def task_that_sometimes_fails(task): + ... if task.host.name == "leaf00.cmh": + ... raise Exception("an uncontrolled exception happened") + ... elif task.host.name == "leaf01.cmh": + ... return Result(host=task.host, result="yikes", failed=True) + ... else: + ... return Result(host=task.host, result="swoosh") + ... + +Finally, when we run the task brigade fails immediately and the traceback is shown on the screen:: + + >>> b = brigade.filter(site="cmh") + >>> r = b.run(task_that_sometimes_fails) + Traceback (most recent call last): + File "", line 1, in + File "/Users/dbarroso/workspace/brigade/brigade/core/__init__.py", line 191, in run + result.raise_on_error() + File "/Users/dbarroso/workspace/brigade/brigade/core/task.py", line 145, in raise_on_error + raise BrigadeExecutionError(self) + brigade.core.exceptions.BrigadeExecutionError: + ######################################## + # host1.cmh (succeeded) + ######################################## + swoosh + ######################################## + # host2.cmh (succeeded) + ######################################## + swoosh + ######################################## + # spine00.cmh (succeeded) + ######################################## + swoosh + ######################################## + # spine01.cmh (succeeded) + ######################################## + swoosh + ######################################## + # leaf00.cmh (failed) + ######################################## + Traceback (most recent call last): + File "/Users/dbarroso/workspace/brigade/brigade/core/__init__.py", line 201, in run_task + r = task._start(host=host, brigade=brigade, dry_run=dry_run) + File "/Users/dbarroso/workspace/brigade/brigade/core/task.py", line 41, in _start + r = self.task(self, **self.params) or Result(host) + File "", line 3, in task_that_sometimes_fails + Exception: an uncontrolled exception happened + + ######################################## + # leaf01.cmh (failed) + ######################################## + yikes + +As with any other exception you can capture it:: + + >>> try: + ... r = b.run(task_that_sometimes_fails) + ... except BrigadeExecutionError as e: + ... error = e + ... + >>> + +Let's inspect the object. You can easily identify the tasks that failed:: + + >>> error.failed_hosts + {'leaf00.cmh': [], 'leaf01.cmh': []} + >>> error.failed_hosts['leaf00.cmh'][0].failed + True + >>> error.failed_hosts['leaf00.cmh'][0].result + 'Traceback (most recent call last):\n File "/Users/dbarroso/workspace/brigade/brigade/core/__init__.py", line 201, in run_task\n r = task._start(host=host, brigade=brigade, dry_run=dry_run)\n File "/Users/dbarroso/workspace/brigade/brigade/core/task.py", line 41, in _start\n r = self.task(self, **self.params) or Result(host)\n File "", line 3, in task_that_sometimes_fails\nException: an uncontrolled exception happened\n' + >>> error.failed_hosts['leaf00.cmh'][0].exception + Exception('an uncontrolled exception happened',) + >>> error.failed_hosts['leaf01.cmh'][0].failed + True + >>> error.failed_hosts['leaf01.cmh'][0].result + 'yikes' + >>> error.failed_hosts['leaf01.cmh'][0].exception + >>> + +Or you can just grab the :obj:`brigade.core.task.AggregatedResult` inside the exception and do something useful with it:: + + >>> error.result.items() + dict_items([('host1.cmh', []), ('host2.cmh', []), ('spine00.cmh', []), ('spine01.cmh', []), ('leaf00.cmh', []), ('leaf01.cmh', [])]) + +Not failing by default +---------------------- + +Now, let's repeat the previous example but setting ``raise_on_error=False``:: + + >>> from brigade.core.task import Result + >>> from brigade.easy import easy_brigade + >>> from brigade.plugins.tasks.text import print_result + >>> + >>> brigade = easy_brigade( + ... host_file="hosts.yaml", group_file="groups.yaml", + ... dry_run=True, + ... raise_on_error=False, + ... ) + >>> + >>> + >>> def task_that_sometimes_fails(task): + ... if task.host.name == "leaf00.cmh": + ... raise Exception("an uncontrolled exception happened") + ... elif task.host.name == "leaf01.cmh": + ... return Result(host=task.host, result="yikes", failed=True) + ... else: + ... return Result(host=task.host, result="swoosh") + ... + >>> + >>> b = brigade.filter(site="cmh") + >>> + >>> r = b.run(task_that_sometimes_fails) + >>> + +If ``raise_on_error=False`` the result of the task will contain a :obj:`brigade.core.task.AggregatedResult` object describing what happened:: + + >>> r["leaf00.cmh"].failed + True + >>> r["leaf00.cmh"].result + 'Traceback (most recent call last):\n File "/Users/dbarroso/workspace/brigade/brigade/core/__init__.py", line 201, in run_task\n r = task._start(host=host, brigade=brigade, dry_run=dry_run)\n File "/Users/dbarroso/workspace/brigade/brigade/core/task.py", line 41, in _start\n r = self.task(self, **self.params) or Result(host)\n File "", line 3, in task_that_sometimes_fails\nException: an uncontrolled exception happened\n' + >>> r["leaf00.cmh"].exception + Exception('an uncontrolled exception happened',) + >>> r["leaf01.cmh"].failed + True + >>> r["leaf01.cmh"].result + 'yikes' + >>> r["leaf01.cmh"].exception + >>> r["host1.cmh"].failed + False + >>> r["host1.cmh"].result + 'swoosh' + +Skipping Hosts +-------------- + +If you set ``raise_on_error=False`` and a task fails ``brigade`` will keep track of the failing hosts and will skip the host in following tasks:: + + >>> r = b.run(task_that_sometimes_fails) + >>> r.failed + True + >>> r.failed + False + +What did just happen? Let's inspect the result:: + + >>> r.skipped + True + >>> r['leaf00.cmh'].failed + False + >>> r['leaf00.cmh'].skipped + True + >>> r['leaf00.cmh'].result + >>> r['leaf01.cmh'].failed + False + >>> r['leaf01.cmh'].skipped + True + >>> r['leaf01.cmh'].result + >>> + +As you can see the second time we ran the same tasks didn't trigger any error because the hosts that failed the first time were skipped. You can inspect which devices are on the "blacklist":: + + >>> b.data.failed_hosts + {'leaf00.cmh', 'leaf01.cmh'} + +And even whitelist them: + + >>> r = b.run(task_that_sometimes_fails) + >>> r['leaf00.cmh'].skipped + True + >>> r['leaf01.cmh'].skipped + False + >>> r['leaf01.cmh'].failed + True + +You can also reset the list of blacklisted hosts:: + + >>> b.data.failed_hosts = set() + >>> r = b.run(task_that_sometimes_fails) + >>> r['leaf00.cmh'].skipped + False + >>> r['leaf00.cmh'].failed + True + >>> r['leaf01.cmh'].skipped + False + >>> r['leaf01.cmh'].failed + True + +``AggreggatedResult`` +--------------------- + +Regardless of if you had ``raise_on_error`` set to ``True`` or ``False`` you will have access to the very same :obj:`brigade.core.task.AggregatedResult` object. The only difference is that in the former case you will have the object in the ``result`` attribute of a :obj:`brigade.core.exceptions.BrigadeExecutionError` object and on the latter you will get it in the assigned variable. + +Let's see a few things you can do with an :obj:`brigade.core.task.AggregatedResult` object:: + + >>> r + AggregatedResult: task_that_sometimes_fails + >>> r.failed + True + >>> r.failed_hosts + {'leaf00.cmh': [], 'leaf01.cmh': []} + >>> r.raise_on_error() + Traceback (most recent call last): + File "", line 1, in + File "/Users/dbarroso/workspace/brigade/brigade/core/task.py", line 145, in raise_on_error + raise BrigadeExecutionError(self) + brigade.core.exceptions.BrigadeExecutionError: + ######################################## + # host1.cmh (succeeded) + ######################################## + swoosh + ######################################## + # host2.cmh (succeeded) + ######################################## + swoosh + ######################################## + # spine00.cmh (succeeded) + ######################################## + swoosh + ######################################## + # spine01.cmh (succeeded) + ######################################## + swoosh + ######################################## + # leaf00.cmh (failed) + ######################################## + Traceback (most recent call last): + File "/Users/dbarroso/workspace/brigade/brigade/core/__init__.py", line 201, in run_task + r = task._start(host=host, brigade=brigade, dry_run=dry_run) + File "/Users/dbarroso/workspace/brigade/brigade/core/task.py", line 41, in _start + r = self.task(self, **self.params) or Result(host) + File "", line 3, in task_that_sometimes_fails + Exception: an uncontrolled exception happened + + ######################################## + # leaf01.cmh (failed) + ######################################## + yikes + +As you can see you can quickly discern if the execution failed and you can even trigger the exception automatically if needed (if no host failed ``r.raise_on_error`` will just return ``None``) + +Overriding default behavior +--------------------------- + +Regardless of the default behavior you can force ``raise_on_error`` on a per task basis:: + + >>> r = b.run(task_that_sometimes_fails, + ... raise_on_error=True) + Traceback (most recent call last): + File "", line 2, in + r = b.run(task_that_sometimes_fails, + raise_on_error=False) + File "/Users/dbarroso/workspace/brigade/brigade/core/__init__.py", line 191, in run + result.raise_on_error() + File "/Users/dbarroso/workspace/brigade/brigade/core/task.py", line 145, in raise_on_error + raise BrigadeExecutionError(self) + brigade.core.exceptions.BrigadeExecutionError: + ######################################## + # host1.cmh (succeeded) + ######################################## + swoosh + ######################################## + # host2.cmh (succeeded) + ######################################## + swoosh + ######################################## + # spine00.cmh (succeeded) + ######################################## + swoosh + ######################################## + # spine01.cmh (succeeded) + ######################################## + swoosh + ######################################## + # leaf00.cmh (failed) + ######################################## + Traceback (most recent call last): + File "/Users/dbarroso/workspace/brigade/brigade/core/__init__.py", line 201, in run_task + r = task._start(host=host, brigade=brigade, dry_run=dry_run) + File "/Users/dbarroso/workspace/brigade/brigade/core/task.py", line 41, in _start + r = self.task(self, **self.params) or Result(host) + File "", line 3, in task_that_sometimes_fails + Exception: an uncontrolled exception happened + + ######################################## + # leaf01.cmh (failed) + ######################################## + yikes + + >>> r = b.run(task_that_sometimes_fails, + ... raise_on_error=False) + >>> + +As you can see, regardless of what ``brigade`` had configured to do, the task failed on the first case but didn't on the second one. + +Which one to use +---------------- + +It dependsâ„¢. As a rule of thumb it's probably safer to fail by default and capture errors explicitly. For instance, a continuation you can see an example where we run a task that can change the system and if it fails we try to run a cleanup operation and if it doesn't succeed either we blacklist the host so further tasks are skipped for that host:: + + try: + brigade.run(task_that_attempts_to_change_the_system) + except BrigadeExecutionError as e: + for host in e.failed_hosts.keys(): + r = brigade.filter(name=host).run(task_that_reverts_changes, + raise_on_error=True) + if r.failed: + brigade.data.failed_hosts.add(host) + +In other simpler cases it might be just simpler and completely safe to ignore errors:: + + r = brigade.run(a_task_that_is_safe_if_it_fails) + brigade.run(print_result, + data=result) diff --git a/docs/tutorials/intro/running_tasks_grouping.rst b/docs/tutorials/intro/running_tasks_grouping.rst new file mode 100644 index 00000000..f3330cf7 --- /dev/null +++ b/docs/tutorials/intro/running_tasks_grouping.rst @@ -0,0 +1,227 @@ +Grouping tasks +============== + +Sometimes it is useful to group tasks either for reusability purposes or to speed up the execution (see :doc:`execution model `). Creating groups of tasks is very easy, for instance:: + + def group_of_tasks(task): + task.run(command, + command="echo hi! I am {host} and I am a {host.nos} device") + task.run(command, + command="echo hi! I am a {host[type]}") + +Groups of tasks are called as regular tasks:: + + >>> b = brigade.filter(site="cmh") + >>> result = b.run(group_of_tasks) + >>> + >>> + >>> b.run(print_result, + ... num_workers=1, + ... data=result, + ... vars=["stdout"]) + * host1.cmh ** changed : False ************************************************* + ---- group_of_tasks ** changed : False ---------------------------------------- + + ---- command ** changed : False ----------------------------------------------- + hi! I am host1.cmh and I am a linux device + + ---- command ** changed : False ----------------------------------------------- + hi! I am a host + + * host2.cmh ** changed : False ************************************************* + ---- group_of_tasks ** changed : False ---------------------------------------- + + ---- command ** changed : False ----------------------------------------------- + hi! I am host2.cmh and I am a linux device + + ---- command ** changed : False ----------------------------------------------- + hi! I am a host + + * spine00.cmh ** changed : False *********************************************** + ---- group_of_tasks ** changed : False ---------------------------------------- + + ---- command ** changed : False ----------------------------------------------- + hi! I am spine00.cmh and I am a eos device + + ---- command ** changed : False ----------------------------------------------- + hi! I am a network_device + + * spine01.cmh ** changed : False *********************************************** + ---- group_of_tasks ** changed : False ---------------------------------------- + + ---- command ** changed : False ----------------------------------------------- + hi! I am spine01.cmh and I am a junos device + + ---- command ** changed : False ----------------------------------------------- + hi! I am a network_device + + * leaf00.cmh ** changed : False ************************************************ + ---- group_of_tasks ** changed : False ---------------------------------------- + + ---- command ** changed : False ----------------------------------------------- + hi! I am leaf00.cmh and I am a eos device + + ---- command ** changed : False ----------------------------------------------- + hi! I am a network_device + + * leaf01.cmh ** changed : False ************************************************ + ---- group_of_tasks ** changed : False ---------------------------------------- + + ---- command ** changed : False ----------------------------------------------- + hi! I am leaf01.cmh and I am a junos device + + ---- command ** changed : False ----------------------------------------------- + hi! I am a network_device + +Groups of tasks return for each host a :obj:`brigade.core.task.MultiResult` object which is a list-like object of :obj:`brigade.core.task.Result`. The object will contain the result for each individual task within the group of tasks:: + + >>> result["leaf01.cmh"].__class__ + + >>> result["leaf01.cmh"][0].name + 'group_of_tasks' + >>> result["leaf01.cmh"][1].name + 'command' + >>> result["leaf01.cmh"][1].result + 'hi! I am leaf01.cmh and I am a junos device\n' + +.. note:: Position ``0`` will be the result for the grouping itself while the rest will be the results for the task inside in the same order as defined in there. + +Groups of tasks can also return their own result if needed:: + + >>> from brigade.core.task import Result + >>> + >>> + >>> def group_of_tasks_with_result(task): + ... task.run(command, + ... command="echo hi! I am {host} and I am a {host.nos} device") + ... task.run(command, + ... command="echo hi! I am a {host[type]}") + ... return Result(host=task.host, result="Yippee ki-yay") + ... + >>> result = b.run(group_of_tasks_with_result) + >>> + >>> result["leaf01.cmh"][0].name + 'group_of_tasks_with_result' + >>> result["leaf01.cmh"][0].result + 'Yippee ki-yay' + +Accessing host data +------------------- + +Something interesting about groupings is that you can access host data from them. For instance:: + + >>> def access_host_data(task): + ... if task.host.nos == "eos": + ... task.host["my-new-var"] = "setting a new var for eos" + ... elif task.host.nos == "junos": + ... task.host["my-new-var"] = "setting a new var for junos" + ... + >>> + >>> b.run(access_host_data) + >>> + >>> b.inventory.hosts["leaf00.cmh"]["my-new-var"] + 'setting a new var for eos' + >>> b.inventory.hosts["leaf01.cmh"]["my-new-var"] + 'setting a new var for junos' + +Reusability +----------- + +We mentioned earlier that groups of tasks where also useful for reusability purposes. Let's see it with an example:: + + >>> def count(task, to): + ... task.run(command, + ... command="echo {}".format(list(range(0, to)))) + ... + +Great, we created a super complex task that can count up to an arbitrary number. Let's count to 10:: + + >>> result = b.run(count, + ... to=10) + >>> + >>> + >>> b.run(print_result, + ... num_workers=1, + ... data=result, + ... vars=["stdout"]) + * host1.cmh ** changed : False ************************************************* + ---- count ** changed : False ------------------------------------------------- + + ---- command ** changed : False ----------------------------------------------- + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + + * host2.cmh ** changed : False ************************************************* + ---- count ** changed : False ------------------------------------------------- + + ---- command ** changed : False ----------------------------------------------- + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + + * spine00.cmh ** changed : False *********************************************** + ---- count ** changed : False ------------------------------------------------- + + ---- command ** changed : False ----------------------------------------------- + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + + * spine01.cmh ** changed : False *********************************************** + ---- count ** changed : False ------------------------------------------------- + + ---- command ** changed : False ----------------------------------------------- + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + + * leaf00.cmh ** changed : False ************************************************ + ---- count ** changed : False ------------------------------------------------- + + ---- command ** changed : False ----------------------------------------------- + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + + * leaf01.cmh ** changed : False ************************************************ + ---- count ** changed : False ------------------------------------------------- + + ---- command ** changed : False ----------------------------------------------- + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + +And now to 20:: + + >>> result = b.run(count, + ... to=20) + >>> + >>> b.run(print_result, + ... num_workers=1, + ... data=result, + ... vars=["stdout"]) + * host1.cmh ** changed : False ************************************************* + ---- count ** changed : False ------------------------------------------------- + + ---- command ** changed : False ----------------------------------------------- + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] + + * host2.cmh ** changed : False ************************************************* + ---- count ** changed : False ------------------------------------------------- + + ---- command ** changed : False ----------------------------------------------- + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] + + * spine00.cmh ** changed : False *********************************************** + ---- count ** changed : False ------------------------------------------------- + + ---- command ** changed : False ----------------------------------------------- + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] + + * spine01.cmh ** changed : False *********************************************** + ---- count ** changed : False ------------------------------------------------- + + ---- command ** changed : False ----------------------------------------------- + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] + + * leaf00.cmh ** changed : False ************************************************ + ---- count ** changed : False ------------------------------------------------- + + ---- command ** changed : False ----------------------------------------------- + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] + + * leaf01.cmh ** changed : False ************************************************ + ---- count ** changed : False ------------------------------------------------- + + ---- command ** changed : False ----------------------------------------------- + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] + diff --git a/examples/1_simple_runbooks/get_facts.py b/examples/1_simple_runbooks/get_facts.py index 5defeda5..89bd5a05 100755 --- a/examples/1_simple_runbooks/get_facts.py +++ b/examples/1_simple_runbooks/get_facts.py @@ -1,26 +1,30 @@ #!/usr/bin/env python """ -This is a very simple scripts to get facts and print them on the screen. +This is a very simple runbook to get facts and print them on the screen. """ -from brigade.core import Brigade -from brigade.core.configuration import Config -from brigade.plugins.inventory.simple import SimpleInventory -from brigade.plugins.tasks import networking, text -brigade = Brigade( - inventory=SimpleInventory("../hosts.yaml", "../groups.yaml"), - dry_run=True, - config=Config(raise_on_error=False), +from brigade.easy import easy_brigade +from brigade.plugins.functions.text import print_title +from brigade.plugins.tasks.networking import napalm_get +from brigade.plugins.tasks.text import print_result + + +brigade = easy_brigade( + hosts="../hosts.yaml", groups="../groups.yaml", + dry_run=True, + raise_on_error=False, ) +print_title("Getting interfaces and facts") + # select which devices we want to work with filtered = brigade.filter(type="network_device", site="cmh") # we are going to gather "interfaces" and "facts" information with napalm -results = filtered.run(networking.napalm_get, +results = filtered.run(napalm_get, getters=["interfaces", "facts"]) # Let's print the result on screen -filtered.run(text.print_result, +filtered.run(print_result, num_workers=1, # we are printing on screen so we want to do this synchronously data=results) diff --git a/examples/Vagrantfile b/examples/inventory/Vagrantfile similarity index 100% rename from examples/Vagrantfile rename to examples/inventory/Vagrantfile diff --git a/examples/groups.yaml b/examples/inventory/groups.yaml similarity index 100% rename from examples/groups.yaml rename to examples/inventory/groups.yaml diff --git a/examples/hosts.yaml b/examples/inventory/hosts.yaml similarity index 100% rename from examples/hosts.yaml rename to examples/inventory/hosts.yaml diff --git a/examples/network_diagram.graffle b/examples/inventory/network_diagram.graffle similarity index 100% rename from examples/network_diagram.graffle rename to examples/inventory/network_diagram.graffle diff --git a/examples/network_diagram.png b/examples/inventory/network_diagram.png similarity index 100% rename from examples/network_diagram.png rename to examples/inventory/network_diagram.png From 308fd67b7cfe3942a1a668aa18f0b3966a6b8a6d Mon Sep 17 00:00:00 2001 From: David Barroso Date: Mon, 15 Jan 2018 21:57:32 +0100 Subject: [PATCH 59/67] bugfix --- brigade/core/__init__.py | 2 +- brigade/plugins/functions/text/__init__.py | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/brigade/core/__init__.py b/brigade/core/__init__.py index fcb67d95..a02ea1ce 100644 --- a/brigade/core/__init__.py +++ b/brigade/core/__init__.py @@ -204,5 +204,5 @@ def run_task(host, brigade, dry_run, task): logger.error("{}: {}".format(host, tb)) r = Result(host, exception=e, result=tb, failed=True) task.results.append(r) - + r.name = task.name return task.results diff --git a/brigade/plugins/functions/text/__init__.py b/brigade/plugins/functions/text/__init__.py index b7a96615..043171f0 100644 --- a/brigade/plugins/functions/text/__init__.py +++ b/brigade/plugins/functions/text/__init__.py @@ -1,7 +1,4 @@ -from colorama import Fore, Style, init - - -init(autoreset=True) +from colorama import Fore, Style def print_title(title): From 57853e29c4b14f042e6fed6cb5a0932b2b2a7d90 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Mon, 15 Jan 2018 21:57:58 +0100 Subject: [PATCH 60/67] allow telling a task to be run on hosts that should be skipped otherwise --- brigade/core/task.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/brigade/core/task.py b/brigade/core/task.py index a65a0343..528e2fff 100644 --- a/brigade/core/task.py +++ b/brigade/core/task.py @@ -22,17 +22,18 @@ class Task(object): dry_run(``bool``): Populated right before calling the ``task`` """ - def __init__(self, task, name=None, **kwargs): + def __init__(self, task, name=None, skipped=False, **kwargs): self.name = name or task.__name__ self.task = task self.params = kwargs + self.skipped = skipped self.results = MultiResult(self.name) def __repr__(self): return self.name def _start(self, host, brigade, dry_run, sub_task=False): - if host.name in brigade.data.failed_hosts: + if host.name in brigade.data.failed_hosts and not self.skipped: r = Result(host, skipped=True) else: self.host = host From 78248715f951472506774cd886efa416ab951309 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Mon, 15 Jan 2018 21:58:22 +0100 Subject: [PATCH 61/67] for consistency with tasks --- brigade/plugins/functions/__init__.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/brigade/plugins/functions/__init__.py b/brigade/plugins/functions/__init__.py index 419dd70e..e69de29b 100644 --- a/brigade/plugins/functions/__init__.py +++ b/brigade/plugins/functions/__init__.py @@ -1,6 +0,0 @@ -from . import text - - -__all__ = ( - "text", -) From b9a0d09db4ed7dc9e422f99ae28e6d3cf5a46457 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Mon, 15 Jan 2018 22:01:49 +0100 Subject: [PATCH 62/67] allow printing multiresult objects --- brigade/plugins/tasks/text/print_result.py | 23 +++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/brigade/plugins/tasks/text/print_result.py b/brigade/plugins/tasks/text/print_result.py index 32d19248..60801c59 100644 --- a/brigade/plugins/tasks/text/print_result.py +++ b/brigade/plugins/tasks/text/print_result.py @@ -1,20 +1,23 @@ import pprint -from brigade.core.task import AggregatedResult, Result +from brigade.core.task import AggregatedResult, MultiResult, Result from colorama import Fore, Style, init -init(autoreset=True) +init(autoreset=True, convert=False, strip=False) -def print_result(task, data, vars=None): +def print_result(task, data, vars=None, failed=None, task_id=None): """ Prints on screen the :obj:`brigade.core.task.Result` from a previous task Arguments: data (:obj:`brigade.core.task.Result`): from a previous task vars (list of str): Which attributes you want to print + failed (``bool``): if ``True`` assume the task failed + task_id (``int``): if we have a :obj:`brigade.core.task.MultiResult` print + only task in this position Returns: :obj:`brigade.core.task.Result`: @@ -26,7 +29,12 @@ def print_result(task, data, vars=None): if isinstance(data, AggregatedResult): data = data[task.host.name] - if data.failed: + if task_id is not None: + r = data[task_id] + data = MultiResult(data.name) + data.append(r) + + if data.failed or failed: color = Fore.RED elif data.changed: color = Fore.YELLOW @@ -41,9 +49,10 @@ def print_result(task, data, vars=None): print("{}{}{}{}".format(Style.BRIGHT, Fore.CYAN, msg, "-" * (80 - len(msg)))) for v in vars: x = getattr(r, v, "") - if r and not isinstance(x, str): - pprint.pprint(x) - elif r: + if x and not isinstance(x, str): + pprint.pprint(x, indent=2) + elif x: print(x) + print() return Result(task.host) From df26d8163a1fe6df5ea7b3299749475b8e10d7fd Mon Sep 17 00:00:00 2001 From: David Barroso Date: Mon, 15 Jan 2018 22:04:41 +0100 Subject: [PATCH 63/67] update docstring --- brigade/core/task.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/brigade/core/task.py b/brigade/core/task.py index 528e2fff..a1af8ad4 100644 --- a/brigade/core/task.py +++ b/brigade/core/task.py @@ -11,10 +11,16 @@ class Task(object): Arguments: task (callable): function or callable we will be calling + name (``string``): name of task, defaults to ``task.__name__`` + skipped (``bool``): whether to run hosts that should be skipped otherwise or not **kwargs: Parameters that will be passed to the ``task`` Attributes: + task (callable): function or callable we will be calling + name (``string``): name of task, defaults to ``task.__name__`` + skipped (``bool``): whether to run hosts that should be skipped otherwise or not params: Parameters that will be passed to the ``task``. + self.results (:obj:`brigade.core.tasks.MultiResult`): Intermediate results host (:obj:`brigade.core.inventory.Host`): Host we are operating with. Populated right before calling the ``task`` brigade(:obj:`brigade.core.Brigade`): Populated right before calling From 707c9cc760d52cf4d2a8d92c7ed652c8a28bb8c3 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Mon, 15 Jan 2018 22:05:28 +0100 Subject: [PATCH 64/67] updated examples --- docs/_static/css/custom.css | 12 + docs/conf.py | 5 +- docs/howto/basic-napalm-getters.rst | 146 --- .../from_runbooks_to_complex_tooling.rst | 11 + docs/howto/index.rst | 2 +- docs/howto/simple_runbooks/backup.ipynb | 1 + docs/howto/simple_runbooks/configure.ipynb | 1 + docs/howto/simple_runbooks/get_facts.ipynb | 1 + docs/howto/simple_runbooks/index.rst | 11 + docs/howto/simple_runbooks/rollback.ipynb | 1 + docs/howto/simple_runbooks/validate.ipynb | 1 + docs/howto/simple_tooling/backup.ipynb | 1 + docs/howto/simple_tooling/configure.ipynb | 1 + docs/howto/simple_tooling/get_facts.ipynb | 1 + docs/howto/simple_tooling/index.rst | 13 + docs/howto/simple_tooling/rollback.ipynb | 1 + docs/howto/simple_tooling/validate.ipynb | 1 + docs/requirements.txt | 2 + examples/1_simple_runbooks/backup.ipynb | 557 +++++++++ examples/1_simple_runbooks/backup.py | 38 +- examples/1_simple_runbooks/configure.ipynb | 673 +++++++++++ examples/1_simple_runbooks/configure.py | 56 +- examples/1_simple_runbooks/get_facts.ipynb | 662 ++++++++++ examples/1_simple_runbooks/get_facts.py | 26 +- examples/1_simple_runbooks/rollback.ipynb | 438 +++++++ examples/1_simple_runbooks/rollback.py | 47 + examples/1_simple_runbooks/validate.ipynb | 689 +++++++++++ examples/1_simple_runbooks/validate.py | 33 +- examples/2_simple_tooling/backup.ipynb | 1062 +++++++++++++++++ examples/2_simple_tooling/backup.py | 57 +- examples/2_simple_tooling/configure.ipynb | 747 ++++++++++++ examples/2_simple_tooling/configure.py | 59 +- examples/2_simple_tooling/get_facts.ipynb | 306 +++++ examples/2_simple_tooling/get_facts.py | 25 +- examples/2_simple_tooling/rollback.ipynb | 411 +++++++ examples/2_simple_tooling/rollback.py | 56 + examples/2_simple_tooling/validate.ipynb | 404 +++++++ examples/2_simple_tooling/validate.py | 35 +- examples/highlighter.py | 34 + 39 files changed, 6362 insertions(+), 265 deletions(-) create mode 100644 docs/_static/css/custom.css delete mode 100644 docs/howto/basic-napalm-getters.rst create mode 100644 docs/howto/from_runbooks_to_complex_tooling.rst create mode 120000 docs/howto/simple_runbooks/backup.ipynb create mode 120000 docs/howto/simple_runbooks/configure.ipynb create mode 120000 docs/howto/simple_runbooks/get_facts.ipynb create mode 100644 docs/howto/simple_runbooks/index.rst create mode 120000 docs/howto/simple_runbooks/rollback.ipynb create mode 120000 docs/howto/simple_runbooks/validate.ipynb create mode 120000 docs/howto/simple_tooling/backup.ipynb create mode 120000 docs/howto/simple_tooling/configure.ipynb create mode 120000 docs/howto/simple_tooling/get_facts.ipynb create mode 100644 docs/howto/simple_tooling/index.rst create mode 120000 docs/howto/simple_tooling/rollback.ipynb create mode 120000 docs/howto/simple_tooling/validate.ipynb create mode 100644 examples/1_simple_runbooks/backup.ipynb create mode 100644 examples/1_simple_runbooks/configure.ipynb create mode 100644 examples/1_simple_runbooks/get_facts.ipynb create mode 100644 examples/1_simple_runbooks/rollback.ipynb create mode 100755 examples/1_simple_runbooks/rollback.py create mode 100644 examples/1_simple_runbooks/validate.ipynb create mode 100644 examples/2_simple_tooling/backup.ipynb create mode 100644 examples/2_simple_tooling/configure.ipynb create mode 100644 examples/2_simple_tooling/get_facts.ipynb create mode 100644 examples/2_simple_tooling/rollback.ipynb create mode 100755 examples/2_simple_tooling/rollback.py create mode 100644 examples/2_simple_tooling/validate.ipynb create mode 100644 examples/highlighter.py diff --git a/docs/_static/css/custom.css b/docs/_static/css/custom.css new file mode 100644 index 00000000..2da34a19 --- /dev/null +++ b/docs/_static/css/custom.css @@ -0,0 +1,12 @@ +div.pygments pre { + font-size: 0.8em; + padding: 0.5em 0.5em 0.5em 0.5em; +} + +span.lineno { + color: gray; +} + +span.lineno::after { + content: "|" +} diff --git a/docs/conf.py b/docs/conf.py index ba2321d7..77bde96b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -38,7 +38,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon'] +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon', 'nbsphinx'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -101,7 +101,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -# html_static_path = ['_static'] +html_static_path = ['_static'] # Custom sidebar templates, must be a dictionary that maps document names # to template names. @@ -190,6 +190,7 @@ def build_configuration_parameters(app): def setup(app): """Map methods to states of the documentation build.""" app.connect('builder-inited', build_configuration_parameters) + app.add_stylesheet('css/custom.css') build_configuration_parameters(None) diff --git a/docs/howto/basic-napalm-getters.rst b/docs/howto/basic-napalm-getters.rst deleted file mode 100644 index dbd65e6e..00000000 --- a/docs/howto/basic-napalm-getters.rst +++ /dev/null @@ -1,146 +0,0 @@ -Gathering information with NAPALM -################################# - -Inventory -========= - -Let's start by seeing how to work with the inventory. Let's assume the following files: - -* Hosts file: - -.. literalinclude:: ../../examples/inventory/hosts.yaml - :name: hosts.yaml - :language: yaml - -* Groups file: - -.. literalinclude:: ../../examples/inventory/groups.yaml - :name: groups.yaml - :language: yaml - -We can instantiate Brigade as follows:: - - >>> from brigade.core import Brigade - >>> from brigade.plugins.inventory.simple import SimpleInventory - >>> brigade = Brigade( - ... inventory=SimpleInventory("hosts.yaml", "groups.yaml"), - ... dry_run=True) - >>> brigade.inventory.hosts.keys() - dict_keys(['host1.cmh', 'host2.cmh', 'switch00.cmh', 'switch01.cmh', 'host1.bma', 'host2.bma', 'switch00.bma', 'switch01.bma']) - >>> brigade.inventory.groups.keys() - dict_keys(['all', 'bma-leaf', 'bma-host', 'bma', 'cmh-leaf', 'cmh-host', 'cmh']) - -As you can see instantiating brigade and providing inventory information is very easy. Now let's see how we can filter hosts. This will be useful when we want to apply certain tasks to only certain devices:: - - >>> brigade.filter(site="cmh").inventory.hosts.keys() - dict_keys(['host1.cmh', 'host2.cmh', 'switch00.cmh', 'switch01.cmh']) - >>> brigade.filter(site="cmh", role="leaf").inventory.hosts.keys() - dict_keys(['switch00.cmh', 'switch01.cmh']) - -You can basically filter by any attribute the device has. The filter is also cumulative:: - - >>> cmh = brigade.filter(site="cmh") - >>> cmh.inventory.hosts.keys() - dict_keys(['host1.cmh', 'host2.cmh', 'switch00.cmh', 'switch01.cmh']) - >>> cmh.filter(role="leaf").inventory.hosts.keys() - dict_keys(['switch00.cmh', 'switch01.cmh']) - -Data -==== - -Now let's see how to access data. Let's start by grabbing a host:: - - >>> host = brigade.inventory.hosts["switch00.cmh"] - -Now, you can access host data either via the host itself, as it behaves like a dict, or via it's ``data`` attribute. The difference is that if access data via the host itself the information will be resolved and data inherited by parent groups will be accessible while if you access the data via the ``data`` attribute only data belonging to the host will be accessible. Let's see a few examples, refer to the files on top of this document for reference:: - - >>> host["nos"] - 'eos' - >>> host.data["nos"] - 'eos' - >>> host["domain"] - 'acme.com' - >>> host.domain["domain"] - Traceback (most recent call last): - File "", line 1, in - AttributeError: 'Host' object has no attribute 'domain' - -You can access the parent group via the ``group`` attribute and :obj:`brigade.core.inventory.Group` behave in the same exact way as :obj:`brigade.core.inventory.Host`:: - - >>> host.group - Group: cmh-leaf - >>> host.group["domain"] - 'acme.com' - >>> host.group.data["domain"] - Traceback (most recent call last): - File "", line 1, in - KeyError: 'domain' - -Tasks -===== - -Now we know how to deal with the inventory let's try to use plugin to gather device information:: - - >>> from brigade.plugins import tasks - >>> cmh_leaf = brigade.filter(site="cmh", role="leaf") - >>> result = cmh_leaf.run(task=tasks.napalm_get_facts, - ... facts="facts") - >>> print(result) - {'switch00.cmh': {'result': {'hostname': 'switch00.cmh', 'fqdn': 'switch00.cmh.cmh.acme.com', 'vendor': 'Arista', 'model': 'vEOS', 'serial_number': '', 'os_version': '4.17.5M-4414219.4175M', 'uptime': 83187, 'interface_list': ['Ethernet1', 'Ethernet2', 'Management1']}}, 'switch01.cmh': {'result': {'vendor': 'Juniper', 'model': 'FIREFLY-PERIMETER', 'serial_number': 'a7defdc362ff', 'os_version': '12.1X47-D20.7', 'hostname': 'switch01.cmh', 'fqdn': 'switch01.cmh.cmh.acme.com', 'uptime': 83084, 'interface_list': ['ge-0/0/0', 'gr-0/0/0', 'ip-0/0/0', 'lsq-0/0/0', 'lt-0/0/0', 'mt-0/0/0', 'sp-0/0/0', 'ge-0/0/1', 'ge-0/0/2', '.local.', 'dsc', 'gre', 'ipip', 'irb', 'lo0', 'lsi', 'mtun', 'pimd', 'pime', 'pp0', 'ppd0', 'ppe0', 'st0', 'tap', 'vlan']}}} - -You can also group multiple tasks into a single block:: - - >>> def get_info(task): - ... # Grouping multiple tasks that go together - ... r = tasks.napalm_get_facts(task, "facts") - ... print(task.host.name) - ... print("============") - ... print(r["result"]) - ... r = tasks.napalm_get_facts(task, "interfaces") - ... print(task.host.name) - ... print("============") - ... print(r["result"]) - ... print() - ... - >>> cmh_leaf.run(task=get_info) - switch00.cmh - ============ - {'hostname': 'switch00.bma', 'fqdn': 'switch00.bma.bma.acme.com', 'vendor': 'Arista', 'model': 'vEOS', 'serial_number': '', 'os_version': '4.17.5M-4414219.4175M', 'uptime': 83424, 'interface_list': ['Ethernet1', 'Ethernet2', 'Management1']} - switch00.cmh - ============ - {'Ethernet2': {'is_up': False, 'is_enabled': False, 'description': 'Another interface in bma', 'last_flapped': 1511034159.0399787, 'speed': 0, 'mac_address': '08:00:27:AB:42:B6'}, 'Management1': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': 1511033376.7964435, 'speed': 1000, 'mac_address': '08:00:27:47:87:83'}, 'Ethernet1': {'is_up': True, 'is_enabled': True, 'description': 'An Interface in bma', 'last_flapped': 1511033362.0302556, 'speed': 0, 'mac_address': '08:00:27:2D:F4:5A'}} - - switch01.cmh - ============ - {'vendor': 'Juniper', 'model': 'FIREFLY-PERIMETER', 'serial_number': 'a7defdc362ff', 'os_version': '12.1X47-D20.7', 'hostname': 'switch01.bma', 'fqdn': 'switch01.bma.bma.acme.com', 'uptime': 83320, 'interface_list': ['ge-0/0/0', 'gr-0/0/0', 'ip-0/0/0', 'lsq-0/0/0', 'lt-0/0/0', 'mt-0/0/0', 'sp-0/0/0', 'ge-0/0/1', 'ge-0/0/2', '.local.', 'dsc', 'gre', 'ipip', 'irb', 'lo0', 'lsi', 'mtun', 'pimd', 'pime', 'pp0', 'ppd0', 'ppe0', 'st0', 'tap', 'vlan']} - switch01.cmh - ============ - {'ge-0/0/0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': 83272.0, 'mac_address': '08:00:27:AA:8C:76', 'speed': 1000}, 'gr-0/0/0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'None', 'speed': 800}, 'ip-0/0/0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'None', 'speed': 800}, 'lsq-0/0/0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': 83273.0, 'mac_address': 'None', 'speed': -1}, 'lt-0/0/0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': '02:96:14:8C:76:B3', 'speed': 800}, 'mt-0/0/0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'None', 'speed': 800}, 'sp-0/0/0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': 83273.0, 'mac_address': 'Unspecified', 'speed': 800}, 'ge-0/0/1': {'is_up': True, 'is_enabled': True, 'description': 'An Interface in bma', 'last_flapped': 83272.0, 'mac_address': '08:00:27:FB:F0:FC', 'speed': 1000}, 'ge-0/0/2': {'is_up': False, 'is_enabled': False, 'description': 'Another interface in bma', 'last_flapped': 82560.0, 'mac_address': '08:00:27:32:60:54', 'speed': 1000}, '.local.': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'Unspecified', 'speed': -1}, 'dsc': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'Unspecified', 'speed': -1}, 'gre': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'None', 'speed': -1}, 'ipip': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'None', 'speed': -1}, 'irb': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': '4C:96:14:8C:76:B0', 'speed': -1}, 'lo0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'Unspecified', 'speed': -1}, 'lsi': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'Unspecified', 'speed': -1}, 'mtun': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'None', 'speed': -1}, 'pimd': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'None', 'speed': -1}, 'pime': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'None', 'speed': -1}, 'pp0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'Unspecified', 'speed': -1}, 'ppd0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'None', 'speed': 800}, 'ppe0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'None', 'speed': 800}, 'st0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'None', 'speed': -1}, 'tap': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'Unspecified', 'speed': -1}, 'vlan': {'is_up': False, 'is_enabled': True, 'description': '', 'last_flapped': 83282.0, 'mac_address': '00:00:00:00:00:00', 'speed': 1000}} - -Or even reuse:: - - >>> def get_facts(task, facts): - ... # variable "facts" will let us reuse this for multiple purposes - ... r = tasks.napalm_get_facts(task, facts) - ... print(task.host.name) - ... print("============") - ... print(r["result"]) - ... print() - ... - >>> cmh_leaf.run(task=get_facts, facts="facts") - switch00.cmh - ============ - {'hostname': 'switch00.bma', 'fqdn': 'switch00.bma.bma.acme.com', 'vendor': 'Arista', 'model': 'vEOS', 'serial_number': '', 'os_version': '4.17.5M-4414219.4175M', 'uptime': 83534, 'interface_list': ['Ethernet1', 'Ethernet2', 'Management1']} - - switch01.cmh - ============ - {'vendor': 'Juniper', 'model': 'FIREFLY-PERIMETER', 'serial_number': 'a7defdc362ff', 'os_version': '12.1X47-D20.7', 'hostname': 'switch01.bma', 'fqdn': 'switch01.bma.bma.acme.com', 'uptime': 83431, 'interface_list': ['ge-0/0/0', 'gr-0/0/0', 'ip-0/0/0', 'lsq-0/0/0', 'lt-0/0/0', 'mt-0/0/0', 'sp-0/0/0', 'ge-0/0/1', 'ge-0/0/2', '.local.', 'dsc', 'gre', 'ipip', 'irb', 'lo0', 'lsi', 'mtun', 'pimd', 'pime', 'pp0', 'ppd0', 'ppe0', 'st0', 'tap', 'vlan']} - - >>> cmh_leaf.run(task=get_facts, facts="interfaces") - switch00.cmh - ============ - {'Ethernet2': {'is_up': False, 'is_enabled': False, 'description': 'Another interface in bma', 'last_flapped': 1511034159.0400095, 'speed': 0, 'mac_address': '08:00:27:AB:42:B6'}, 'Management1': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': 1511033376.7963786, 'speed': 1000, 'mac_address': '08:00:27:47:87:83'}, 'Ethernet1': {'is_up': True, 'is_enabled': True, 'description': 'An Interface in bma', 'last_flapped': 1511033362.0302918, 'speed': 0, 'mac_address': '08:00:27:2D:F4:5A'}} - - switch01.cmh - ============ - {'ge-0/0/0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': 83387.0, 'mac_address': '08:00:27:AA:8C:76', 'speed': 1000}, 'gr-0/0/0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'None', 'speed': 800}, 'ip-0/0/0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'None', 'speed': 800}, 'lsq-0/0/0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': 83388.0, 'mac_address': 'None', 'speed': -1}, 'lt-0/0/0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': '02:96:14:8C:76:B3', 'speed': 800}, 'mt-0/0/0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'None', 'speed': 800}, 'sp-0/0/0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': 83388.0, 'mac_address': 'Unspecified', 'speed': 800}, 'ge-0/0/1': {'is_up': True, 'is_enabled': True, 'description': 'An Interface in bma', 'last_flapped': 83387.0, 'mac_address': '08:00:27:FB:F0:FC', 'speed': 1000}, 'ge-0/0/2': {'is_up': False, 'is_enabled': False, 'description': 'Another interface in bma', 'last_flapped': 82675.0, 'mac_address': '08:00:27:32:60:54', 'speed': 1000}, '.local.': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'Unspecified', 'speed': -1}, 'dsc': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'Unspecified', 'speed': -1}, 'gre': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'None', 'speed': -1}, 'ipip': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'None', 'speed': -1}, 'irb': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': '4C:96:14:8C:76:B0', 'speed': -1}, 'lo0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'Unspecified', 'speed': -1}, 'lsi': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'Unspecified', 'speed': -1}, 'mtun': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'None', 'speed': -1}, 'pimd': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'None', 'speed': -1}, 'pime': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'None', 'speed': -1}, 'pp0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'Unspecified', 'speed': -1}, 'ppd0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'None', 'speed': 800}, 'ppe0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'None', 'speed': 800}, 'st0': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'None', 'speed': -1}, 'tap': {'is_up': True, 'is_enabled': True, 'description': '', 'last_flapped': -1.0, 'mac_address': 'Unspecified', 'speed': -1}, 'vlan': {'is_up': False, 'is_enabled': True, 'description': '', 'last_flapped': 83397.0, 'mac_address': '00:00:00:00:00:00', 'speed': 1000}} diff --git a/docs/howto/from_runbooks_to_complex_tooling.rst b/docs/howto/from_runbooks_to_complex_tooling.rst new file mode 100644 index 00000000..3f56fe61 --- /dev/null +++ b/docs/howto/from_runbooks_to_complex_tooling.rst @@ -0,0 +1,11 @@ +From Runbooks to Advanced Tooling +================================= + +In this section we are going to build advanced tooling in a series of baby steps. We will start writing very simple runbooks, then we will slightly rewrite those runbooks to turn them into flexible cli tools. Once we are done with that we will turn those isolated cli tools into an advanced tool that can accommodate different workflows. + +.. toctree:: + :maxdepth: 1 + :glob: + + simple_runbooks/index + simple_tooling/index diff --git a/docs/howto/index.rst b/docs/howto/index.rst index 69843994..dfadc113 100644 --- a/docs/howto/index.rst +++ b/docs/howto/index.rst @@ -5,4 +5,4 @@ How to use Brigade :maxdepth: 1 :glob: - * \ No newline at end of file + * diff --git a/docs/howto/simple_runbooks/backup.ipynb b/docs/howto/simple_runbooks/backup.ipynb new file mode 120000 index 00000000..f6561d19 --- /dev/null +++ b/docs/howto/simple_runbooks/backup.ipynb @@ -0,0 +1 @@ +../../../examples/1_simple_runbooks/backup.ipynb \ No newline at end of file diff --git a/docs/howto/simple_runbooks/configure.ipynb b/docs/howto/simple_runbooks/configure.ipynb new file mode 120000 index 00000000..9fc2fc70 --- /dev/null +++ b/docs/howto/simple_runbooks/configure.ipynb @@ -0,0 +1 @@ +../../../examples/1_simple_runbooks/configure.ipynb \ No newline at end of file diff --git a/docs/howto/simple_runbooks/get_facts.ipynb b/docs/howto/simple_runbooks/get_facts.ipynb new file mode 120000 index 00000000..cdf882f1 --- /dev/null +++ b/docs/howto/simple_runbooks/get_facts.ipynb @@ -0,0 +1 @@ +../../../examples/1_simple_runbooks/get_facts.ipynb \ No newline at end of file diff --git a/docs/howto/simple_runbooks/index.rst b/docs/howto/simple_runbooks/index.rst new file mode 100644 index 00000000..f63c4e42 --- /dev/null +++ b/docs/howto/simple_runbooks/index.rst @@ -0,0 +1,11 @@ +Simple Runbooks +=============== + +In this series we are going to build a few simple runbooks to do various tasks on the network. Each runbook is going to do one specific tasks and is going to make certain assumptions to simplify the logic as possible; like which devices are involved, where is the data located, etc. In following series we will build on these runbooks to build more flexible and complex tooling. + +.. toctree:: + :maxdepth: 1 + :glob: + + * + diff --git a/docs/howto/simple_runbooks/rollback.ipynb b/docs/howto/simple_runbooks/rollback.ipynb new file mode 120000 index 00000000..4081ad20 --- /dev/null +++ b/docs/howto/simple_runbooks/rollback.ipynb @@ -0,0 +1 @@ +../../../examples/1_simple_runbooks/rollback.ipynb \ No newline at end of file diff --git a/docs/howto/simple_runbooks/validate.ipynb b/docs/howto/simple_runbooks/validate.ipynb new file mode 120000 index 00000000..91119d86 --- /dev/null +++ b/docs/howto/simple_runbooks/validate.ipynb @@ -0,0 +1 @@ +../../../examples/1_simple_runbooks/validate.ipynb \ No newline at end of file diff --git a/docs/howto/simple_tooling/backup.ipynb b/docs/howto/simple_tooling/backup.ipynb new file mode 120000 index 00000000..4cd21f71 --- /dev/null +++ b/docs/howto/simple_tooling/backup.ipynb @@ -0,0 +1 @@ +../../../examples/2_simple_tooling/backup.ipynb \ No newline at end of file diff --git a/docs/howto/simple_tooling/configure.ipynb b/docs/howto/simple_tooling/configure.ipynb new file mode 120000 index 00000000..71c86d66 --- /dev/null +++ b/docs/howto/simple_tooling/configure.ipynb @@ -0,0 +1 @@ +../../../examples/2_simple_tooling/configure.ipynb \ No newline at end of file diff --git a/docs/howto/simple_tooling/get_facts.ipynb b/docs/howto/simple_tooling/get_facts.ipynb new file mode 120000 index 00000000..450584f1 --- /dev/null +++ b/docs/howto/simple_tooling/get_facts.ipynb @@ -0,0 +1 @@ +../../../examples/2_simple_tooling/get_facts.ipynb \ No newline at end of file diff --git a/docs/howto/simple_tooling/index.rst b/docs/howto/simple_tooling/index.rst new file mode 100644 index 00000000..4a338209 --- /dev/null +++ b/docs/howto/simple_tooling/index.rst @@ -0,0 +1,13 @@ +Simple Tooling +============== + +In this series we are going to build on top of the runbooks we built on :doc:`the previous section <../simple_runbooks/index>` and build more versatile tooling. Most tools will be not that very different from its equivalent runbook so you should be able to look at them side by side and realize how easy it was to build a cli tool from a previous runbook thanks to the fact that brigade is a native python framework and integrates natively with other frameworks like `click `. + + +.. toctree:: + :maxdepth: 1 + :glob: + + * + + diff --git a/docs/howto/simple_tooling/rollback.ipynb b/docs/howto/simple_tooling/rollback.ipynb new file mode 120000 index 00000000..bd29b917 --- /dev/null +++ b/docs/howto/simple_tooling/rollback.ipynb @@ -0,0 +1 @@ +../../../examples/2_simple_tooling/rollback.ipynb \ No newline at end of file diff --git a/docs/howto/simple_tooling/validate.ipynb b/docs/howto/simple_tooling/validate.ipynb new file mode 120000 index 00000000..e7b38cfd --- /dev/null +++ b/docs/howto/simple_tooling/validate.ipynb @@ -0,0 +1 @@ +../../../examples/2_simple_tooling/validate.ipynb \ No newline at end of file diff --git a/docs/requirements.txt b/docs/requirements.txt index 6fab96cb..e28da7c4 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,6 +1,8 @@ sphinx sphinx_rtd_theme sphinxcontrib-napoleon +jupyter +nbsphinx -r ../requirements.txt future # jtextfsm diff --git a/examples/1_simple_runbooks/backup.ipynb b/examples/1_simple_runbooks/backup.ipynb new file mode 100644 index 00000000..8f5956ec --- /dev/null +++ b/examples/1_simple_runbooks/backup.ipynb @@ -0,0 +1,557 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# ignore this cell, this is just a helper cell to provide the magic %highlight_file\n", + "%run ../highlighter.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Backup\n", + "\n", + "This runbook is going to download the configuration of the devices and save it under `./backup/$hostname`. It also reports changes as we will a continuation.\n", + "\n", + "## Code" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "

 1 #!/usr/bin/env python\n",
+       " 2 """\n",
+       " 3 Runbook that downloads the configuration from the devices and\n",
+       " 4 stores them on disk.\n",
+       " 5 """\n",
+       " 6 from brigade.easy import easy_brigade\n",
+       " 7 from brigade.plugins.tasks import files, networking, text\n",
+       " 8 \n",
+       " 9 \n",
+       "10 def backup(task):\n",
+       "11     """\n",
+       "12     This function groups two tasks:\n",
+       "13         1. Download configuration from the device\n",
+       "14         2. Store to disk\n",
+       "15     """\n",
+       "16     result = task.run(networking.napalm_get,\n",
+       "17                       name="Gathering configuration",\n",
+       "18                       getters="config")\n",
+       "19 \n",
+       "20     task.run(files.write,\n",
+       "21              name="Saving Configuration to disk",\n",
+       "22              content=result.result["config"]["running"],\n",
+       "23              filename="./backups/{}".format(task.host))\n",
+       "24 \n",
+       "25 \n",
+       "26 brg = easy_brigade(\n",
+       "27         host_file="../inventory/hosts.yaml",\n",
+       "28         group_file="../inventory/groups.yaml",\n",
+       "29         dry_run=False,\n",
+       "30         raise_on_error=True,\n",
+       "31 )\n",
+       "32 \n",
+       "33 # select which devices we want to work with\n",
+       "34 filtered = brg.filter(type="network_device", site="cmh")\n",
+       "35 \n",
+       "36 # Run the ``backup`` function that groups the tasks to\n",
+       "37 # download/store devices' configuration\n",
+       "38 results = filtered.run(backup,\n",
+       "39                        name="Backing up configurations")\n",
+       "40 \n",
+       "41 # Let's print the result on screen\n",
+       "42 filtered.run(text.print_result,\n",
+       "43              num_workers=1,  # task should be done synchronously\n",
+       "44              data=results,\n",
+       "45              task_id=-1,  # we only want to print the last task\n",
+       "46              )\n",
+       "
\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%highlight_file backup.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Demo\n", + "\n", + "Let's run the command for the first time (note we are cleaning first `./backups/` folder to pretend each run of the following cell is the first one):" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[33m* spine00.cmh ** changed : True ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Saving Configuration to disk ** changed : True ---------------------------\u001b[0m\n", + "\u001b[0m--- ./backups/spine00.cmh\n", + "\n", + "+++ new\n", + "\n", + "@@ -0,0 +1,34 @@\n", + "\n", + "+! Command: show running-config\n", + "+! device: localhost (vEOS, EOS-4.17.5M)\n", + "+!\n", + "+! boot system flash:/vEOS-lab.swi\n", + "+!\n", + "+event-handler dhclient\n", + "+ trigger on-boot\n", + "+ action bash sudo /mnt/flash/initialize_ma1.sh\n", + "+!\n", + "+transceiver qsfp default-mode 4x10G\n", + "+!\n", + "+spanning-tree mode mstp\n", + "+!\n", + "+aaa authorization exec default local\n", + "+!\n", + "+aaa root secret sha512 $6$5stn7z2imBLV6iO0$w0ZnOhy8SwNdELdO2da9q8wDKerYTyY8evY052UoyRJ2Wo6liaUneuTFGphL8JQD9gtESOipCBb6PYmSMuUjs.\n", + "+!\n", + "+username admin privilege 15 role network-admin secret sha512 $6$qkXlQpatVlanYe9v$aHTbPaGTaqDRCp5WSC3DPpDfblYSE24.OHeKgGOOTf0.Ol2lDpivTvHByx5tU41sVOGcHqc4U4LgrKv8AjbKQ/\n", + "+username vagrant privilege 15 role network-admin secret sha512 $6$kRQZJTqx69hOW5ag$Y6VX8Kk37TWEsriKdr6ixqvMuUSSbuFu2Eh/5SIet2TCeXP3bdlwikIAruPp6lHB5HdC.t6tPsZVctHMU7H590\n", + "+!\n", + "+interface Ethernet1\n", + "+!\n", + "+interface Ethernet2\n", + "+!\n", + "+interface Management1\n", + "+ ip address 10.0.2.15/24\n", + "+!\n", + "+no ip routing\n", + "+!\n", + "+management api http-commands\n", + "+ no shutdown\n", + "+!\n", + "+!\n", + "+end\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* spine01.cmh ** changed : True ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Saving Configuration to disk ** changed : True ---------------------------\u001b[0m\n", + "\u001b[0m--- ./backups/spine01.cmh\n", + "\n", + "+++ new\n", + "\n", + "@@ -0,0 +1,70 @@\n", + "\n", + "+\n", + "+## Last commit: 2018-01-14 14:33:48 UTC by vagrant\n", + "+version 12.1X47-D20.7;\n", + "+system {\n", + "+ host-name vsrx;\n", + "+ root-authentication {\n", + "+ encrypted-password \"$1$5MhDFyrI$NBBMndW1POqbN.0QEA4z0.\";\n", + "+ ssh-rsa \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDi/i8iiAZsXC5qdmJZpTxKjUyyoMgEGoHXl/TMFdJjSV+XAZ18OXAEsvPO0AlXJ6RZTwK8Zcr6TLq4l1Kssd+kVN02shFkgDo3wWf3I2BXKKdog6/6fbhiD1SgCeafzWBlUQvREgDQDy1XSFjNjSJ39vtOa8ikqGdbf4XH0hjoLHYDV0H0VNZLboULCNFPF0PHQfPrsp2AXHU+p7sl61GhZgfw6WuLIzXWqJyq9B0Q5XgdmvnvdjZeTOShoPTPbaRYVVFOMGTqJQOZsl5P3wTIJT8JG7iEz1Tiar8nmltON83sy/lEODhZkJPXe3zw3fwUIS9yQ53z0t1UGHm7KGNX vagrant\";\n", + "+ }\n", + "+ login {\n", + "+ user vagrant {\n", + "+ uid 2000;\n", + "+ class super-user;\n", + "+ authentication {\n", + "+ ssh-rsa \"ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key\";\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ services {\n", + "+ ssh {\n", + "+ root-login allow;\n", + "+ }\n", + "+ netconf {\n", + "+ ssh;\n", + "+ }\n", + "+ web-management {\n", + "+ http {\n", + "+ interface ge-0/0/0.0;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ syslog {\n", + "+ user * {\n", + "+ any emergency;\n", + "+ }\n", + "+ file messages {\n", + "+ any any;\n", + "+ authorization info;\n", + "+ }\n", + "+ file interactive-commands {\n", + "+ interactive-commands any;\n", + "+ }\n", + "+ }\n", + "+ license {\n", + "+ autoupdate {\n", + "+ url https://ae1.juniper.net/junos/key_retrieval;\n", + "+ }\n", + "+ }\n", + "+}\n", + "+interfaces {\n", + "+ ge-0/0/0 {\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ dhcp;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+}\n", + "+security {\n", + "+ forwarding-options {\n", + "+ family {\n", + "+ inet6 {\n", + "+ mode packet-based;\n", + "+ }\n", + "+ mpls {\n", + "+ mode packet-based;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* leaf00.cmh ** changed : True *************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Saving Configuration to disk ** changed : True ---------------------------\u001b[0m\n", + "\u001b[0m--- ./backups/leaf00.cmh\n", + "\n", + "+++ new\n", + "\n", + "@@ -0,0 +1,34 @@\n", + "\n", + "+! Command: show running-config\n", + "+! device: localhost (vEOS, EOS-4.17.5M)\n", + "+!\n", + "+! boot system flash:/vEOS-lab.swi\n", + "+!\n", + "+event-handler dhclient\n", + "+ trigger on-boot\n", + "+ action bash sudo /mnt/flash/initialize_ma1.sh\n", + "+!\n", + "+transceiver qsfp default-mode 4x10G\n", + "+!\n", + "+spanning-tree mode mstp\n", + "+!\n", + "+aaa authorization exec default local\n", + "+!\n", + "+aaa root secret sha512 $6$sRifRAo/DXihW7sG$3r4MMTsslNCCWdD/FFIw3lvnnkI4SWO0bvhEzvWSurrOBgUsxjrmgN5kywH5Ta7LNNXiWjFfjwoyefn9nqeB2/\n", + "+!\n", + "+username admin privilege 15 role network-admin secret sha512 $6$/K1M3ENrC/xALAOm$1vCB5TfaI8ih5GQRCwhRE7KGzmc.EGuQZ7dEuwhP7AJC0/A97u88miINH/7GtrBpRZ.Inn5JY9tuymMcmyyKc.\n", + "+username vagrant privilege 15 role network-admin secret sha512 $6$9CGTCvCiiJK3lDMp$kU9ncPDBkw0w09.h9wIhQtMAkZ/1zD1ds/wlAZAtmSQf5ntNMjDgvmZpBcXWAPAETlk4.kA9niLTVmQwaLBV/.\n", + "+!\n", + "+interface Ethernet1\n", + "+!\n", + "+interface Ethernet2\n", + "+!\n", + "+interface Management1\n", + "+ ip address 10.0.2.15/24\n", + "+!\n", + "+no ip routing\n", + "+!\n", + "+management api http-commands\n", + "+ no shutdown\n", + "+!\n", + "+!\n", + "+end\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* leaf01.cmh ** changed : True *************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Saving Configuration to disk ** changed : True ---------------------------\u001b[0m\n", + "\u001b[0m--- ./backups/leaf01.cmh\n", + "\n", + "+++ new\n", + "\n", + "@@ -0,0 +1,70 @@\n", + "\n", + "+\n", + "+## Last commit: 2018-01-14 14:33:48 UTC by vagrant\n", + "+version 12.1X47-D20.7;\n", + "+system {\n", + "+ host-name vsrx;\n", + "+ root-authentication {\n", + "+ encrypted-password \"$1$5MhDFyrI$NBBMndW1POqbN.0QEA4z0.\";\n", + "+ ssh-rsa \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDsfGpEhGi8CbjIHJkMju/CJH6IuQiIzZyDt+AVieDfXKWDuBSOfc7YV8xNdYMqQqpDOWmEVZ7dhfD6IWDI3aa6WLkEXORD+zScjQo+5iHty6VlI61ImHQkWhWX6pZi3Cq/JsH8oldIC2xvzFNWB2p1suu+rzuGtJjbDq5NMlp1bNSiBgV0dHZR6Lt1UuK/rVBl7FbBN8HpInM+a37SkkwIrKMK8z42Ax9ufd17P3SqZP8oo+Ql4Y3aeCz2t4CfZNh9YRLZSiUYF16VN+31mzKEqT7+0rFlyfv/CaPwyfAv2BPFljUEsyFsWU923EGYQsfOIKVnd+zzHDHIHapVMQbh vagrant\";\n", + "+ }\n", + "+ login {\n", + "+ user vagrant {\n", + "+ uid 2000;\n", + "+ class super-user;\n", + "+ authentication {\n", + "+ ssh-rsa \"ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key\";\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ services {\n", + "+ ssh {\n", + "+ root-login allow;\n", + "+ }\n", + "+ netconf {\n", + "+ ssh;\n", + "+ }\n", + "+ web-management {\n", + "+ http {\n", + "+ interface ge-0/0/0.0;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ syslog {\n", + "+ user * {\n", + "+ any emergency;\n", + "+ }\n", + "+ file messages {\n", + "+ any any;\n", + "+ authorization info;\n", + "+ }\n", + "+ file interactive-commands {\n", + "+ interactive-commands any;\n", + "+ }\n", + "+ }\n", + "+ license {\n", + "+ autoupdate {\n", + "+ url https://ae1.juniper.net/junos/key_retrieval;\n", + "+ }\n", + "+ }\n", + "+}\n", + "+interfaces {\n", + "+ ge-0/0/0 {\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ dhcp;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+}\n", + "+security {\n", + "+ forwarding-options {\n", + "+ family {\n", + "+ inet6 {\n", + "+ mode packet-based;\n", + "+ }\n", + "+ mpls {\n", + "+ mode packet-based;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%rm backups/* > /dev/null\n", + "%run backup.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's run it again to see how ``brigade`` detects no changes in the backup:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[34m* spine00.cmh ** changed : False ***********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Saving Configuration to disk ** changed : False --------------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* spine01.cmh ** changed : False ***********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Saving Configuration to disk ** changed : False --------------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* leaf00.cmh ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Saving Configuration to disk ** changed : False --------------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* leaf01.cmh ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Saving Configuration to disk ** changed : False --------------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run backup.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's change the device's hostname and run the backup tool again:\n", + "\n", + " localhost(config)#hostname blah\n", + " blah(config)# end" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[33m* spine00.cmh ** changed : True ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Saving Configuration to disk ** changed : True ---------------------------\u001b[0m\n", + "\u001b[0m--- ./backups/spine00.cmh\n", + "\n", + "+++ new\n", + "\n", + "@@ -1,5 +1,5 @@\n", + "\n", + " ! Command: show running-config\n", + "-! device: localhost (vEOS, EOS-4.17.5M)\n", + "+! device: blah (vEOS, EOS-4.17.5M)\n", + " !\n", + " ! boot system flash:/vEOS-lab.swi\n", + " !\n", + "@@ -8,6 +8,8 @@\n", + "\n", + " action bash sudo /mnt/flash/initialize_ma1.sh\n", + " !\n", + " transceiver qsfp default-mode 4x10G\n", + "+!\n", + "+hostname blah\n", + " !\n", + " spanning-tree mode mstp\n", + " !\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* spine01.cmh ** changed : False ***********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Saving Configuration to disk ** changed : False --------------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* leaf00.cmh ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Saving Configuration to disk ** changed : False --------------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* leaf01.cmh ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Saving Configuration to disk ** changed : False --------------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run backup.py" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/1_simple_runbooks/backup.py b/examples/1_simple_runbooks/backup.py index 84f247f1..baec0417 100755 --- a/examples/1_simple_runbooks/backup.py +++ b/examples/1_simple_runbooks/backup.py @@ -1,28 +1,46 @@ #!/usr/bin/env python +""" +Runbook that downloads the configuration from the devices and +stores them on disk. +""" from brigade.easy import easy_brigade from brigade.plugins.tasks import files, networking, text def backup(task): + """ + This function groups two tasks: + 1. Download configuration from the device + 2. Store to disk + """ result = task.run(networking.napalm_get, + name="Gathering configuration", getters="config") - return task.run(files.write, - content=result.result["config"]["running"], - filename="./backups/{}".format(task.host)) + task.run(files.write, + name="Saving Configuration to disk", + content=result.result["config"]["running"], + filename="./backups/{}".format(task.host)) -brigade = easy_brigade( - hosts="../hosts.yaml", groups="../groups.yaml", +brg = easy_brigade( + host_file="../inventory/hosts.yaml", + group_file="../inventory/groups.yaml", dry_run=False, - raise_on_error=False, + raise_on_error=True, ) # select which devices we want to work with -filtered = brigade.filter(type="network_device", site="cmh") -results = filtered.run(backup) +filtered = brg.filter(type="network_device", site="cmh") + +# Run the ``backup`` function that groups the tasks to +# download/store devices' configuration +results = filtered.run(backup, + name="Backing up configurations") # Let's print the result on screen filtered.run(text.print_result, - num_workers=1, # we are printing on screen so we want to do this synchronously - data=results) + num_workers=1, # task should be done synchronously + data=results, + task_id=-1, # we only want to print the last task + ) diff --git a/examples/1_simple_runbooks/configure.ipynb b/examples/1_simple_runbooks/configure.ipynb new file mode 100644 index 00000000..2f409b64 --- /dev/null +++ b/examples/1_simple_runbooks/configure.ipynb @@ -0,0 +1,673 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "# ignore this cell, this is just a helper cell to provide the magic %highlight_file\n", + "%run ../highlighter.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Configure\n", + "\n", + "This is a runbook to configure the network. To do so we are going to load first some data from the directory `../extra_data/` and then a bunch of templates to generate, based on that extra data, the configuration for the devices.\n", + "\n", + "## Extra data\n", + "\n", + "Let's first look at the extra data we are going to use:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "../extra_data/leaf00.cmh:\r\n", + "l3.yaml\r\n", + "\r\n", + "../extra_data/leaf01.cmh:\r\n", + "l3.yaml\r\n", + "\r\n", + "../extra_data/spine00.cmh:\r\n", + "l3.yaml\r\n", + "\r\n", + "../extra_data/spine01.cmh:\r\n", + "l3.yaml\r\n" + ] + } + ], + "source": [ + "%ls ../extra_data/*" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's look at one of the files for reference:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "---\r\n", + "interfaces:\r\n", + " Ethernet1:\r\n", + " connects_to: spine00.cmh\r\n", + " ipv4: 10.0.0.1/31\r\n", + " enabled: false\r\n", + " Ethernet2:\r\n", + " connects_to: spine01.cmh\r\n", + " ipv4: 10.0.1.1/31\r\n", + " enabled: true\r\n", + "\r\n", + "sessions:\r\n", + " - ipv4: 10.0.0.0\r\n", + " peer_as: 65000\r\n", + " - ipv4: 10.0.1.0\r\n", + " peer_as: 65000\r\n" + ] + } + ], + "source": [ + "% cat ../extra_data/leaf00.cmh/l3.yaml" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Templates\n", + "\n", + "To configure the network we will transform the data we saw before into actual configurationusing jinja2 templates. The templates are:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "../templates/eos:\r\n", + "base.j2 interfaces.j2 leaf.j2 routing.j2 spine.j2\r\n", + "\r\n", + "../templates/junos:\r\n", + "base.j2 interfaces.j2 leaf.j2 routing.j2 spine.j2\r\n" + ] + } + ], + "source": [ + "%ls ../templates/*" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As an example, let's look how the ``interfaces.j2`` template will consume the extra data we saw before to configure the interfaces:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{% for interface, data in l3.interfaces.items() %}\r\n", + "interface {{ interface }}\r\n", + " no switchport\r\n", + " ip address {{ data.ipv4 }}\r\n", + " description link to {{ data.connects_to }}\r\n", + " {{ \"no\" if data.enabled else \"\" }} shutdown\r\n", + "{% endfor %}\r\n", + "\r\n" + ] + } + ], + "source": [ + "%cat ../templates/eos/interfaces.j2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Code\n", + "\n", + "Now let's look at the code that will sticth everything together:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
 1 #!/usr/bin/env python\n",
+       " 2 """\n",
+       " 3 Runbook to configure datacenter\n",
+       " 4 """\n",
+       " 5 from brigade.easy import easy_brigade\n",
+       " 6 from brigade.plugins.functions.text import print_title\n",
+       " 7 from brigade.plugins.tasks import data, networking, text\n",
+       " 8 \n",
+       " 9 \n",
+       "10 def configure(task):\n",
+       "11     """\n",
+       "12     This function groups all the tasks needed to configure the\n",
+       "13     network:\n",
+       "14 \n",
+       "15         1. Loading extra data\n",
+       "16         2. Templates to build configuration\n",
+       "17         3. Deploy configuration on the device\n",
+       "18     """\n",
+       "19     r = task.run(text.template_file,\n",
+       "20                  name="Base Configuration",\n",
+       "21                  template="base.j2",\n",
+       "22                  path="../templates/{brigade_nos}")\n",
+       "23     # r.result holds the result of rendering the template\n",
+       "24     # we store in the host itself so we can keep updating\n",
+       "25     # it as we render other templates\n",
+       "26     task.host["config"] = r.result\n",
+       "27 \n",
+       "28     r = task.run(data.load_yaml,\n",
+       "29                  name="Loading extra data",\n",
+       "30                  file="../extra_data/{host}/l3.yaml")\n",
+       "31     # r.result holds the data contained in the yaml files\n",
+       "32     # we load the data inside the host itself for further use\n",
+       "33     task.host["l3"] = r.result\n",
+       "34 \n",
+       "35     r = task.run(text.template_file,\n",
+       "36                  name="Interfaces Configuration",\n",
+       "37                  template="interfaces.j2",\n",
+       "38                  path="../templates/{brigade_nos}")\n",
+       "39     # we update our hosts' config\n",
+       "40     task.host["config"] += r.result\n",
+       "41 \n",
+       "42     r = task.run(text.template_file,\n",
+       "43                  name="Routing Configuration",\n",
+       "44                  template="routing.j2",\n",
+       "45                  path="../templates/{brigade_nos}")\n",
+       "46     # we update our hosts' config\n",
+       "47     task.host["config"] += r.result\n",
+       "48 \n",
+       "49     r = task.run(text.template_file,\n",
+       "50                  name="Role-specific Configuration",\n",
+       "51                  template="{role}.j2",\n",
+       "52                  path="../templates/{brigade_nos}")\n",
+       "53     # we update our hosts' config\n",
+       "54     task.host["config"] += r.result\n",
+       "55 \n",
+       "56     task.run(networking.napalm_configure,\n",
+       "57              name="Loading Configuration on the device",\n",
+       "58              replace=False,\n",
+       "59              configuration=task.host["config"])\n",
+       "60 \n",
+       "61 \n",
+       "62 brg = easy_brigade(\n",
+       "63         host_file="../inventory/hosts.yaml",\n",
+       "64         group_file="../inventory/groups.yaml",\n",
+       "65         dry_run=False,\n",
+       "66         raise_on_error=True,\n",
+       "67 )\n",
+       "68 \n",
+       "69 \n",
+       "70 # select which devices we want to work with\n",
+       "71 filtered = brg.filter(type="network_device", site="cmh")\n",
+       "72 \n",
+       "73 results = filtered.run(task=configure)\n",
+       "74 \n",
+       "75 print_title("Playbook to configure the network")\n",
+       "76 filtered.run(text.print_result,\n",
+       "77              name="Configure device",\n",
+       "78              num_workers=1,  # task should be done synchronously\n",
+       "79              data=results,\n",
+       "80              task_id=-1,  # we only want to print the last task\n",
+       "81              )\n",
+       "
\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%highlight_file configure.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Demo\n", + "\n", + "Finally let's see everything in action:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[32m**** Playbook to configure the network *****************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* spine00.cmh ** changed : True ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m@@ -8,7 +8,8 @@\n", + " !\n", + " transceiver qsfp default-mode 4x10G\n", + " !\n", + "-hostname localhost\n", + "+hostname spine00.cmh\n", + "+ip domain-name cmh.acme.com\n", + " !\n", + " spanning-tree mode mstp\n", + " !\n", + "@@ -20,13 +21,28 @@\n", + " username vagrant privilege 15 role network-admin secret sha512 $6$kRQZJTqx69hOW5ag$Y6VX8Kk37TWEsriKdr6ixqvMuUSSbuFu2Eh/5SIet2TCeXP3bdlwikIAruPp6lHB5HdC.t6tPsZVctHMU7H590\n", + " !\n", + " interface Ethernet1\n", + "+ description link to leaf00.cmh\n", + "+ no switchport\n", + "+ ip address 10.0.0.0/31\n", + " !\n", + " interface Ethernet2\n", + "+ description link to leaf01.cmh\n", + "+ no switchport\n", + "+ ip address 10.0.0.2/31\n", + " !\n", + " interface Management1\n", + " ip address 10.0.2.15/24\n", + " !\n", + "-no ip routing\n", + "+ip routing\n", + "+!\n", + "+router bgp 65000\n", + "+ neighbor 10.0.0.1 remote-as 65100\n", + "+ neighbor 10.0.0.1 maximum-routes 12000 \n", + "+ neighbor 10.0.0.3 remote-as 65101\n", + "+ neighbor 10.0.0.3 maximum-routes 12000 \n", + "+ address-family ipv4\n", + "+ neighbor 10.0.0.1 activate\n", + "+ neighbor 10.0.0.3 activate\n", + " !\n", + " management api http-commands\n", + " no shutdown\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* spine01.cmh ** changed : True ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m[edit system]\n", + "- host-name vsrx;\n", + "+ host-name spine01.cmh;\n", + "+ domain-name cmh.acme.com;\n", + "[edit interfaces]\n", + "+ ge-0/0/1 {\n", + "+ description \"link to leaf00.cmh\";\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ address 10.0.1.0/31;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ ge-0/0/2 {\n", + "+ description \"link to leaf01.cmh\";\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ address 10.0.1.2/31;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "[edit]\n", + "+ routing-options {\n", + "+ autonomous-system 65000;\n", + "+ }\n", + "+ protocols {\n", + "+ bgp {\n", + "+ import PERMIT_ALL;\n", + "+ export PERMIT_ALL;\n", + "+ group peers {\n", + "+ neighbor 10.0.1.1 {\n", + "+ peer-as 65100;\n", + "+ }\n", + "+ neighbor 10.0.1.3 {\n", + "+ peer-as 65101;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ policy-options {\n", + "+ policy-statement PERMIT_ALL {\n", + "+ from protocol bgp;\n", + "+ then accept;\n", + "+ }\n", + "+ }\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* leaf00.cmh ** changed : True *************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m@@ -8,6 +8,9 @@\n", + " !\n", + " transceiver qsfp default-mode 4x10G\n", + " !\n", + "+hostname leaf00.cmh\n", + "+ip domain-name cmh.acme.com\n", + "+!\n", + " spanning-tree mode mstp\n", + " !\n", + " aaa authorization exec default local\n", + "@@ -17,14 +20,36 @@\n", + " username admin privilege 15 role network-admin secret sha512 $6$/K1M3ENrC/xALAOm$1vCB5TfaI8ih5GQRCwhRE7KGzmc.EGuQZ7dEuwhP7AJC0/A97u88miINH/7GtrBpRZ.Inn5JY9tuymMcmyyKc.\n", + " username vagrant privilege 15 role network-admin secret sha512 $6$9CGTCvCiiJK3lDMp$kU9ncPDBkw0w09.h9wIhQtMAkZ/1zD1ds/wlAZAtmSQf5ntNMjDgvmZpBcXWAPAETlk4.kA9niLTVmQwaLBV/.\n", + " !\n", + "+vlan 100\n", + "+ name frontend\n", + "+!\n", + "+vlan 200\n", + "+ name backend\n", + "+!\n", + " interface Ethernet1\n", + "+ description link to spine00.cmh\n", + "+ shutdown\n", + "+ no switchport\n", + "+ ip address 10.0.0.1/31\n", + " !\n", + " interface Ethernet2\n", + "+ description link to spine01.cmh\n", + "+ no switchport\n", + "+ ip address 10.0.1.1/31\n", + " !\n", + " interface Management1\n", + " ip address 10.0.2.15/24\n", + " !\n", + "-no ip routing\n", + "+ip routing\n", + "+!\n", + "+router bgp 65100\n", + "+ neighbor 10.0.0.0 remote-as 65000\n", + "+ neighbor 10.0.0.0 maximum-routes 12000 \n", + "+ neighbor 10.0.1.0 remote-as 65000\n", + "+ neighbor 10.0.1.0 maximum-routes 12000 \n", + "+ address-family ipv4\n", + "+ neighbor 10.0.0.0 activate\n", + "+ neighbor 10.0.1.0 activate\n", + " !\n", + " management api http-commands\n", + " no shutdown\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* leaf01.cmh ** changed : True *************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m[edit system]\n", + "- host-name vsrx;\n", + "+ host-name leaf01.cmh;\n", + "+ domain-name cmh.acme.com;\n", + "[edit interfaces]\n", + "+ ge-0/0/1 {\n", + "+ description \"link to spine00.cmh\";\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ address 10.0.0.3/31;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ ge-0/0/2 {\n", + "+ description \"link to spine01.cmh\";\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ address 10.0.1.3/31;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "[edit]\n", + "+ routing-options {\n", + "+ autonomous-system 65101;\n", + "+ }\n", + "+ protocols {\n", + "+ bgp {\n", + "+ import PERMIT_ALL;\n", + "+ export PERMIT_ALL;\n", + "+ group peers {\n", + "+ neighbor 10.0.0.2 {\n", + "+ peer-as 65000;\n", + "+ }\n", + "+ neighbor 10.0.1.2 {\n", + "+ peer-as 65000;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ policy-options {\n", + "+ policy-statement PERMIT_ALL {\n", + "+ from protocol bgp;\n", + "+ then accept;\n", + "+ }\n", + "+ }\n", + "+ vlans {\n", + "+ backend {\n", + "+ vlan-id 200;\n", + "+ }\n", + "+ frontend {\n", + "+ vlan-id 100;\n", + "+ }\n", + "+ }\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run configure.py" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[32m**** Playbook to configure the network *****************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* spine00.cmh ** changed : False ***********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : False -------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* spine01.cmh ** changed : False ***********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : False -------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* leaf00.cmh ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : False -------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* leaf01.cmh ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : False -------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run configure.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The tool also detects unwanted changes and corrects them. For instance, let's change the hostname manually:\n", + "\n", + " spine00.cmh((config)#hostname localhost\n", + " localhost(config)#\n", + "\n", + "And run the runbook again:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[32m**** Playbook to configure the network *****************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* spine00.cmh ** changed : True ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m@@ -8,7 +8,7 @@\n", + " !\n", + " transceiver qsfp default-mode 4x10G\n", + " !\n", + "-hostname localhost\n", + "+hostname spine00.cmh\n", + " ip domain-name cmh.acme.com\n", + " !\n", + " spanning-tree mode mstp\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* spine01.cmh ** changed : False ***********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : False -------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* leaf00.cmh ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : False -------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* leaf01.cmh ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : False -------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run configure.py" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/1_simple_runbooks/configure.py b/examples/1_simple_runbooks/configure.py index 9f44d08a..d120a723 100755 --- a/examples/1_simple_runbooks/configure.py +++ b/examples/1_simple_runbooks/configure.py @@ -1,53 +1,81 @@ #!/usr/bin/env python """ -In this example we write a CLI tool with brigade and click to deploy configuration. +Runbook to configure datacenter """ -from brigade.core import Brigade -from brigade.core.configuration import Config -from brigade.plugins.inventory.simple import SimpleInventory +from brigade.easy import easy_brigade +from brigade.plugins.functions.text import print_title from brigade.plugins.tasks import data, networking, text def configure(task): + """ + This function groups all the tasks needed to configure the + network: + + 1. Loading extra data + 2. Templates to build configuration + 3. Deploy configuration on the device + """ r = task.run(text.template_file, + name="Base Configuration", template="base.j2", path="../templates/{brigade_nos}") + # r.result holds the result of rendering the template + # we store in the host itself so we can keep updating + # it as we render other templates task.host["config"] = r.result r = task.run(data.load_yaml, + name="Loading extra data", file="../extra_data/{host}/l3.yaml") + # r.result holds the data contained in the yaml files + # we load the data inside the host itself for further use task.host["l3"] = r.result r = task.run(text.template_file, + name="Interfaces Configuration", template="interfaces.j2", path="../templates/{brigade_nos}") + # we update our hosts' config task.host["config"] += r.result r = task.run(text.template_file, + name="Routing Configuration", template="routing.j2", path="../templates/{brigade_nos}") + # we update our hosts' config task.host["config"] += r.result r = task.run(text.template_file, + name="Role-specific Configuration", template="{role}.j2", path="../templates/{brigade_nos}") + # we update our hosts' config task.host["config"] += r.result - return task.run(networking.napalm_configure, - replace=False, - configuration=task.host["config"]) + task.run(networking.napalm_configure, + name="Loading Configuration on the device", + replace=False, + configuration=task.host["config"]) -brigade = Brigade( - inventory=SimpleInventory("../hosts.yaml", "../groups.yaml"), - dry_run=False, - config=Config(raise_on_error=False), +brg = easy_brigade( + host_file="../inventory/hosts.yaml", + group_file="../inventory/groups.yaml", + dry_run=False, + raise_on_error=True, ) -filtered = brigade.filter(type="network_device", site="cmh") + +# select which devices we want to work with +filtered = brg.filter(type="network_device", site="cmh") results = filtered.run(task=configure) +print_title("Playbook to configure the network") filtered.run(text.print_result, - num_workers=1, # we are printing on screen so we want to do this synchronously - data=results) + name="Configure device", + num_workers=1, # task should be done synchronously + data=results, + task_id=-1, # we only want to print the last task + ) diff --git a/examples/1_simple_runbooks/get_facts.ipynb b/examples/1_simple_runbooks/get_facts.ipynb new file mode 100644 index 00000000..920b6eba --- /dev/null +++ b/examples/1_simple_runbooks/get_facts.ipynb @@ -0,0 +1,662 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# ignore this cell, this is just a helper cell to provide the magic %highlight_file\n", + "%run ../highlighter.py" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "hide_input": true + }, + "source": [ + "# Get Facts" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Following runbook will connect to devices in the site \"cmh\" and gather information about basic facts and interfaces.\n", + "\n", + "## Code" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
 1 #!/usr/bin/env python\n",
+       " 2 """\n",
+       " 3 Very simple runbook to get facts and print them on the screen.\n",
+       " 4 """\n",
+       " 5 \n",
+       " 6 from brigade.easy import easy_brigade\n",
+       " 7 from brigade.plugins.functions.text import print_title\n",
+       " 8 from brigade.plugins.tasks import networking, text\n",
+       " 9 \n",
+       "10 \n",
+       "11 brg = easy_brigade(\n",
+       "12         host_file="../inventory/hosts.yaml",\n",
+       "13         group_file="../inventory/groups.yaml",\n",
+       "14         dry_run=False,\n",
+       "15         raise_on_error=False,\n",
+       "16 )\n",
+       "17 \n",
+       "18 print_title("Getting interfaces and facts")\n",
+       "19 \n",
+       "20 # select which devices we want to work with\n",
+       "21 filtered = brg.filter(type="network_device", site="cmh")\n",
+       "22 \n",
+       "23 # we are going to gather "interfaces" and "facts"\n",
+       "24 # information with napalm\n",
+       "25 results = filtered.run(networking.napalm_get,\n",
+       "26                        getters=["interfaces", "facts"])\n",
+       "27 \n",
+       "28 # Let's print the result on screen\n",
+       "29 filtered.run(text.print_result,\n",
+       "30              num_workers=1,  # task should be done synchronously\n",
+       "31              data=results,\n",
+       "32              )\n",
+       "
\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%highlight_file get_facts.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Demo\n", + "\n", + "Let's run it:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[32m**** Getting interfaces and facts **********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* spine00.cmh ** changed : False ***********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- napalm_get ** changed : False --------------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'facts'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'fqdn'\u001b[0m: \u001b[0m'spine00.cmh.cmh.acme.com'\u001b[0m,\n", + " \u001b[0m'hostname'\u001b[0m: \u001b[0m'spine00.cmh'\u001b[0m,\n", + " \u001b[0m'interface_list'\u001b[0m: \u001b[0m['Ethernet1', 'Ethernet2', 'Management1']\u001b[0m,\n", + " \u001b[0m'model'\u001b[0m: \u001b[0m'vEOS'\u001b[0m,\n", + " \u001b[0m'os_version'\u001b[0m: \u001b[0m'4.17.5M-4414219.4175M'\u001b[0m,\n", + " \u001b[0m'serial_number'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'uptime'\u001b[0m: \u001b[0m76742\u001b[0m,\n", + " \u001b[0m'vendor'\u001b[0m: \u001b[0m'Arista'\u001b[0m}\u001b[0m,\n", + " \u001b[0m'interfaces'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'Ethernet1'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m'link to leaf00.cmh'\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m1516010990.2331386\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'08:00:27:0C:31:79'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m0\u001b[0m}\u001b[0m,\n", + " \u001b[0m'Ethernet2'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m'link to leaf01.cmh'\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m1515939602.295958\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'08:00:27:0C:31:79'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m0\u001b[0m}\u001b[0m,\n", + " \u001b[0m'Management1'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m1515939616.808321\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'08:00:27:47:87:83'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m1000\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* spine01.cmh ** changed : False ***********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- napalm_get ** changed : False --------------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'facts'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'fqdn'\u001b[0m: \u001b[0m'spine01.cmh.cmh.acme.com'\u001b[0m,\n", + " \u001b[0m'hostname'\u001b[0m: \u001b[0m'spine01.cmh'\u001b[0m,\n", + " \u001b[0m'interface_list'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m\u001b[0m'ge-0/0/0'\u001b[0m,\n", + " \u001b[0m'gr-0/0/0'\u001b[0m,\n", + " \u001b[0m'ip-0/0/0'\u001b[0m,\n", + " \u001b[0m'lsq-0/0/0'\u001b[0m,\n", + " \u001b[0m'lt-0/0/0'\u001b[0m,\n", + " \u001b[0m'mt-0/0/0'\u001b[0m,\n", + " \u001b[0m'sp-0/0/0'\u001b[0m,\n", + " \u001b[0m'ge-0/0/1'\u001b[0m,\n", + " \u001b[0m'ge-0/0/2'\u001b[0m,\n", + " \u001b[0m'.local.'\u001b[0m,\n", + " \u001b[0m'dsc'\u001b[0m,\n", + " \u001b[0m'gre'\u001b[0m,\n", + " \u001b[0m'ipip'\u001b[0m,\n", + " \u001b[0m'irb'\u001b[0m,\n", + " \u001b[0m'lo0'\u001b[0m,\n", + " \u001b[0m'lsi'\u001b[0m,\n", + " \u001b[0m'mtun'\u001b[0m,\n", + " \u001b[0m'pimd'\u001b[0m,\n", + " \u001b[0m'pime'\u001b[0m,\n", + " \u001b[0m'pp0'\u001b[0m,\n", + " \u001b[0m'ppd0'\u001b[0m,\n", + " \u001b[0m'ppe0'\u001b[0m,\n", + " \u001b[0m'st0'\u001b[0m,\n", + " \u001b[0m'tap'\u001b[0m,\n", + " \u001b[0m'vlan'\u001b[0m]\u001b[0m,\n", + " \u001b[0m'model'\u001b[0m: \u001b[0m'FIREFLY-PERIMETER'\u001b[0m,\n", + " \u001b[0m'os_version'\u001b[0m: \u001b[0m'12.1X47-D20.7'\u001b[0m,\n", + " \u001b[0m'serial_number'\u001b[0m: \u001b[0m'5efd44465d10'\u001b[0m,\n", + " \u001b[0m'uptime'\u001b[0m: \u001b[0m76648\u001b[0m,\n", + " \u001b[0m'vendor'\u001b[0m: \u001b[0m'Juniper'\u001b[0m}\u001b[0m,\n", + " \u001b[0m'interfaces'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'.local.'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'Unspecified'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'dsc'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'Unspecified'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'ge-0/0/0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m76597.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'08:00:27:AA:8C:76'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m1000\u001b[0m}\u001b[0m,\n", + " \u001b[0m'ge-0/0/1'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m'link to leaf00.cmh'\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m76597.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'08:00:27:1A:7F:BF'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m1000\u001b[0m}\u001b[0m,\n", + " \u001b[0m'ge-0/0/2'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m'link to leaf01.cmh'\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m76597.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'08:00:27:70:E5:81'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m1000\u001b[0m}\u001b[0m,\n", + " \u001b[0m'gr-0/0/0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m800\u001b[0m}\u001b[0m,\n", + " \u001b[0m'gre'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'ip-0/0/0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m800\u001b[0m}\u001b[0m,\n", + " \u001b[0m'ipip'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'irb'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'4C:96:14:8C:76:B0'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'lo0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'Unspecified'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'lsi'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'Unspecified'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'lsq-0/0/0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m76598.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'lt-0/0/0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'02:96:14:8C:76:B3'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m800\u001b[0m}\u001b[0m,\n", + " \u001b[0m'mt-0/0/0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m800\u001b[0m}\u001b[0m,\n", + " \u001b[0m'mtun'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'pimd'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'pime'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'pp0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'Unspecified'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'ppd0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m800\u001b[0m}\u001b[0m,\n", + " \u001b[0m'ppe0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m800\u001b[0m}\u001b[0m,\n", + " \u001b[0m'sp-0/0/0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m76598.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'Unspecified'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m800\u001b[0m}\u001b[0m,\n", + " \u001b[0m'st0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'tap'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'Unspecified'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'vlan'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m76607.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'00:00:00:00:00:00'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m1000\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* leaf00.cmh ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- napalm_get ** changed : False --------------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'facts'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'fqdn'\u001b[0m: \u001b[0m'leaf00.cmh.cmh.acme.com'\u001b[0m,\n", + " \u001b[0m'hostname'\u001b[0m: \u001b[0m'leaf00.cmh'\u001b[0m,\n", + " \u001b[0m'interface_list'\u001b[0m: \u001b[0m['Ethernet1', 'Ethernet2', 'Management1']\u001b[0m,\n", + " \u001b[0m'model'\u001b[0m: \u001b[0m'vEOS'\u001b[0m,\n", + " \u001b[0m'os_version'\u001b[0m: \u001b[0m'4.17.5M-4414219.4175M'\u001b[0m,\n", + " \u001b[0m'serial_number'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'uptime'\u001b[0m: \u001b[0m76556\u001b[0m,\n", + " \u001b[0m'vendor'\u001b[0m: \u001b[0m'Arista'\u001b[0m}\u001b[0m,\n", + " \u001b[0m'interfaces'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'Ethernet1'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m'link to spine00.cmh'\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m1516010957.639385\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'08:00:27:0C:31:79'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m0\u001b[0m}\u001b[0m,\n", + " \u001b[0m'Ethernet2'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m'link to spine01.cmh'\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m1515939788.3633773\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'08:00:27:0C:31:79'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m0\u001b[0m}\u001b[0m,\n", + " \u001b[0m'Management1'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m1515939804.0736248\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'08:00:27:47:87:83'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m1000\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* leaf01.cmh ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- napalm_get ** changed : False --------------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'facts'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'fqdn'\u001b[0m: \u001b[0m'leaf01.cmh.cmh.acme.com'\u001b[0m,\n", + " \u001b[0m'hostname'\u001b[0m: \u001b[0m'leaf01.cmh'\u001b[0m,\n", + " \u001b[0m'interface_list'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m\u001b[0m'ge-0/0/0'\u001b[0m,\n", + " \u001b[0m'gr-0/0/0'\u001b[0m,\n", + " \u001b[0m'ip-0/0/0'\u001b[0m,\n", + " \u001b[0m'lsq-0/0/0'\u001b[0m,\n", + " \u001b[0m'lt-0/0/0'\u001b[0m,\n", + " \u001b[0m'mt-0/0/0'\u001b[0m,\n", + " \u001b[0m'sp-0/0/0'\u001b[0m,\n", + " \u001b[0m'ge-0/0/1'\u001b[0m,\n", + " \u001b[0m'ge-0/0/2'\u001b[0m,\n", + " \u001b[0m'.local.'\u001b[0m,\n", + " \u001b[0m'dsc'\u001b[0m,\n", + " \u001b[0m'gre'\u001b[0m,\n", + " \u001b[0m'ipip'\u001b[0m,\n", + " \u001b[0m'irb'\u001b[0m,\n", + " \u001b[0m'lo0'\u001b[0m,\n", + " \u001b[0m'lsi'\u001b[0m,\n", + " \u001b[0m'mtun'\u001b[0m,\n", + " \u001b[0m'pimd'\u001b[0m,\n", + " \u001b[0m'pime'\u001b[0m,\n", + " \u001b[0m'pp0'\u001b[0m,\n", + " \u001b[0m'ppd0'\u001b[0m,\n", + " \u001b[0m'ppe0'\u001b[0m,\n", + " \u001b[0m'st0'\u001b[0m,\n", + " \u001b[0m'tap'\u001b[0m,\n", + " \u001b[0m'vlan'\u001b[0m]\u001b[0m,\n", + " \u001b[0m'model'\u001b[0m: \u001b[0m'FIREFLY-PERIMETER'\u001b[0m,\n", + " \u001b[0m'os_version'\u001b[0m: \u001b[0m'12.1X47-D20.7'\u001b[0m,\n", + " \u001b[0m'serial_number'\u001b[0m: \u001b[0m'9d842799f666'\u001b[0m,\n", + " \u001b[0m'uptime'\u001b[0m: \u001b[0m76460\u001b[0m,\n", + " \u001b[0m'vendor'\u001b[0m: \u001b[0m'Juniper'\u001b[0m}\u001b[0m,\n", + " \u001b[0m'interfaces'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'.local.'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'Unspecified'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'dsc'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'Unspecified'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'ge-0/0/0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m76407.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'08:00:27:AA:8C:76'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m1000\u001b[0m}\u001b[0m,\n", + " \u001b[0m'ge-0/0/1'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m'link to spine00.cmh'\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m76407.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'08:00:27:A0:42:60'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m1000\u001b[0m}\u001b[0m,\n", + " \u001b[0m'ge-0/0/2'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m'link to spine01.cmh'\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m76407.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'08:00:27:00:6D:5A'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m1000\u001b[0m}\u001b[0m,\n", + " \u001b[0m'gr-0/0/0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m800\u001b[0m}\u001b[0m,\n", + " \u001b[0m'gre'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'ip-0/0/0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m800\u001b[0m}\u001b[0m,\n", + " \u001b[0m'ipip'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'irb'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'4C:96:14:8C:76:B0'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'lo0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'Unspecified'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'lsi'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'Unspecified'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'lsq-0/0/0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m76408.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'lt-0/0/0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'02:96:14:8C:76:B3'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m800\u001b[0m}\u001b[0m,\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \u001b[0m'mt-0/0/0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m800\u001b[0m}\u001b[0m,\n", + " \u001b[0m'mtun'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'pimd'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'pime'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'pp0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'Unspecified'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'ppd0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m800\u001b[0m}\u001b[0m,\n", + " \u001b[0m'ppe0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m800\u001b[0m}\u001b[0m,\n", + " \u001b[0m'sp-0/0/0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m76408.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'Unspecified'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m800\u001b[0m}\u001b[0m,\n", + " \u001b[0m'st0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'None'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'tap'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m-1.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'Unspecified'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m-1\u001b[0m}\u001b[0m,\n", + " \u001b[0m'vlan'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m76417.0\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'00:00:00:00:00:00'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m1000\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run get_facts.py" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/1_simple_runbooks/get_facts.py b/examples/1_simple_runbooks/get_facts.py index 89bd5a05..5b308998 100755 --- a/examples/1_simple_runbooks/get_facts.py +++ b/examples/1_simple_runbooks/get_facts.py @@ -1,30 +1,32 @@ #!/usr/bin/env python """ -This is a very simple runbook to get facts and print them on the screen. +Very simple runbook to get facts and print them on the screen. """ from brigade.easy import easy_brigade from brigade.plugins.functions.text import print_title -from brigade.plugins.tasks.networking import napalm_get -from brigade.plugins.tasks.text import print_result +from brigade.plugins.tasks import networking, text -brigade = easy_brigade( - hosts="../hosts.yaml", groups="../groups.yaml", - dry_run=True, +brg = easy_brigade( + host_file="../inventory/hosts.yaml", + group_file="../inventory/groups.yaml", + dry_run=False, raise_on_error=False, ) print_title("Getting interfaces and facts") # select which devices we want to work with -filtered = brigade.filter(type="network_device", site="cmh") +filtered = brg.filter(type="network_device", site="cmh") -# we are going to gather "interfaces" and "facts" information with napalm -results = filtered.run(napalm_get, +# we are going to gather "interfaces" and "facts" +# information with napalm +results = filtered.run(networking.napalm_get, getters=["interfaces", "facts"]) # Let's print the result on screen -filtered.run(print_result, - num_workers=1, # we are printing on screen so we want to do this synchronously - data=results) +filtered.run(text.print_result, + num_workers=1, # task should be done synchronously + data=results, + ) diff --git a/examples/1_simple_runbooks/rollback.ipynb b/examples/1_simple_runbooks/rollback.ipynb new file mode 100644 index 00000000..e99f693f --- /dev/null +++ b/examples/1_simple_runbooks/rollback.ipynb @@ -0,0 +1,438 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# ignore this cell, this is just a helper cell to provide the magic %highlight_file\n", + "%run ../highlighter.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Rollback\n", + "\n", + "This runbook plays well with the ``backup.py`` one. You can basically backup the configuration, and then roll it back with this runbook if things don't go as expected.\n", + "\n", + "## Code" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
 1 #!/usr/bin/env python\n",
+       " 2 """\n",
+       " 3 Runbook to rollback configuration from a saved configuration\n",
+       " 4 """\n",
+       " 5 from brigade.easy import easy_brigade\n",
+       " 6 from brigade.plugins.tasks import networking, text\n",
+       " 7 \n",
+       " 8 \n",
+       " 9 def rollback(task):\n",
+       "10     """\n",
+       "11     This function loads the backup from ./backups/$hostname and\n",
+       "12     deploys it.\n",
+       "13     """\n",
+       "14     task.run(networking.napalm_configure,\n",
+       "15              name="Loading Configuration on the device",\n",
+       "16              replace=True,\n",
+       "17              filename="backups/{host}")\n",
+       "18 \n",
+       "19 \n",
+       "20 brg = easy_brigade(\n",
+       "21         host_file="../inventory/hosts.yaml",\n",
+       "22         group_file="../inventory/groups.yaml",\n",
+       "23         dry_run=False,\n",
+       "24         raise_on_error=True,\n",
+       "25 )\n",
+       "26 \n",
+       "27 \n",
+       "28 # select which devices we want to work with\n",
+       "29 filtered = brg.filter(type="network_device", site="cmh")\n",
+       "30 \n",
+       "31 results = filtered.run(task=rollback)\n",
+       "32 \n",
+       "33 filtered.run(text.print_result,\n",
+       "34              num_workers=1,  # task should be done synchronously\n",
+       "35              data=results,\n",
+       "36              task_id=-1,  # we only want to print the last task\n",
+       "37              )\n",
+       "
\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%highlight_file rollback.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Demo\n", + "\n", + "So let's rollback to the backup configuration we took before configuring the network early on:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[33m* spine00.cmh ** changed : True ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m@@ -8,8 +8,7 @@\n", + " !\n", + " transceiver qsfp default-mode 4x10G\n", + " !\n", + "-hostname spine00.cmh\n", + "-ip domain-name cmh.acme.com\n", + "+hostname blah\n", + " !\n", + " spanning-tree mode mstp\n", + " !\n", + "@@ -21,28 +20,13 @@\n", + " username vagrant privilege 15 role network-admin secret sha512 $6$kRQZJTqx69hOW5ag$Y6VX8Kk37TWEsriKdr6ixqvMuUSSbuFu2Eh/5SIet2TCeXP3bdlwikIAruPp6lHB5HdC.t6tPsZVctHMU7H590\n", + " !\n", + " interface Ethernet1\n", + "- description link to leaf00.cmh\n", + "- no switchport\n", + "- ip address 10.0.0.0/31\n", + " !\n", + " interface Ethernet2\n", + "- description link to leaf01.cmh\n", + "- no switchport\n", + "- ip address 10.0.0.2/31\n", + " !\n", + " interface Management1\n", + " ip address 10.0.2.15/24\n", + " !\n", + "-ip routing\n", + "-!\n", + "-router bgp 65000\n", + "- neighbor 10.0.0.1 remote-as 65100\n", + "- neighbor 10.0.0.1 maximum-routes 12000 \n", + "- neighbor 10.0.0.3 remote-as 65101\n", + "- neighbor 10.0.0.3 maximum-routes 12000 \n", + "- address-family ipv4\n", + "- neighbor 10.0.0.1 activate\n", + "- neighbor 10.0.0.3 activate\n", + "+no ip routing\n", + " !\n", + " management api http-commands\n", + " no shutdown\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* spine01.cmh ** changed : True ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m[edit system]\n", + "- host-name spine01.cmh;\n", + "+ host-name vsrx;\n", + "- domain-name cmh.acme.com;\n", + "[edit interfaces]\n", + "- ge-0/0/1 {\n", + "- description \"link to leaf00.cmh\";\n", + "- unit 0 {\n", + "- family inet {\n", + "- address 10.0.1.0/31;\n", + "- }\n", + "- }\n", + "- }\n", + "- ge-0/0/2 {\n", + "- description \"link to leaf01.cmh\";\n", + "- unit 0 {\n", + "- family inet {\n", + "- address 10.0.1.2/31;\n", + "- }\n", + "- }\n", + "- }\n", + "[edit]\n", + "- routing-options {\n", + "- autonomous-system 65000;\n", + "- }\n", + "- protocols {\n", + "- bgp {\n", + "- import PERMIT_ALL;\n", + "- export PERMIT_ALL;\n", + "- group peers {\n", + "- neighbor 10.0.1.1 {\n", + "- peer-as 65100;\n", + "- }\n", + "- neighbor 10.0.1.3 {\n", + "- peer-as 65101;\n", + "- }\n", + "- }\n", + "- }\n", + "- }\n", + "- policy-options {\n", + "- policy-statement PERMIT_ALL {\n", + "- from protocol bgp;\n", + "- then accept;\n", + "- }\n", + "- }\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* leaf00.cmh ** changed : True *************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m@@ -8,9 +8,6 @@\n", + " !\n", + " transceiver qsfp default-mode 4x10G\n", + " !\n", + "-hostname leaf00.cmh\n", + "-ip domain-name cmh.acme.com\n", + "-!\n", + " spanning-tree mode mstp\n", + " !\n", + " aaa authorization exec default local\n", + "@@ -20,36 +17,14 @@\n", + " username admin privilege 15 role network-admin secret sha512 $6$/K1M3ENrC/xALAOm$1vCB5TfaI8ih5GQRCwhRE7KGzmc.EGuQZ7dEuwhP7AJC0/A97u88miINH/7GtrBpRZ.Inn5JY9tuymMcmyyKc.\n", + " username vagrant privilege 15 role network-admin secret sha512 $6$9CGTCvCiiJK3lDMp$kU9ncPDBkw0w09.h9wIhQtMAkZ/1zD1ds/wlAZAtmSQf5ntNMjDgvmZpBcXWAPAETlk4.kA9niLTVmQwaLBV/.\n", + " !\n", + "-vlan 100\n", + "- name frontend\n", + "-!\n", + "-vlan 200\n", + "- name backend\n", + "-!\n", + " interface Ethernet1\n", + "- description link to spine00.cmh\n", + "- shutdown\n", + "- no switchport\n", + "- ip address 10.0.0.1/31\n", + " !\n", + " interface Ethernet2\n", + "- description link to spine01.cmh\n", + "- no switchport\n", + "- ip address 10.0.1.1/31\n", + " !\n", + " interface Management1\n", + " ip address 10.0.2.15/24\n", + " !\n", + "-ip routing\n", + "-!\n", + "-router bgp 65100\n", + "- neighbor 10.0.0.0 remote-as 65000\n", + "- neighbor 10.0.0.0 maximum-routes 12000 \n", + "- neighbor 10.0.1.0 remote-as 65000\n", + "- neighbor 10.0.1.0 maximum-routes 12000 \n", + "- address-family ipv4\n", + "- neighbor 10.0.0.0 activate\n", + "- neighbor 10.0.1.0 activate\n", + "+no ip routing\n", + " !\n", + " management api http-commands\n", + " no shutdown\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* leaf01.cmh ** changed : True *************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m[edit system]\n", + "- host-name leaf01.cmh;\n", + "+ host-name vsrx;\n", + "- domain-name cmh.acme.com;\n", + "[edit interfaces]\n", + "- ge-0/0/1 {\n", + "- description \"link to spine00.cmh\";\n", + "- unit 0 {\n", + "- family inet {\n", + "- address 10.0.0.3/31;\n", + "- }\n", + "- }\n", + "- }\n", + "- ge-0/0/2 {\n", + "- description \"link to spine01.cmh\";\n", + "- unit 0 {\n", + "- family inet {\n", + "- address 10.0.1.3/31;\n", + "- }\n", + "- }\n", + "- }\n", + "[edit]\n", + "- routing-options {\n", + "- autonomous-system 65101;\n", + "- }\n", + "- protocols {\n", + "- bgp {\n", + "- import PERMIT_ALL;\n", + "- export PERMIT_ALL;\n", + "- group peers {\n", + "- neighbor 10.0.0.2 {\n", + "- peer-as 65000;\n", + "- }\n", + "- neighbor 10.0.1.2 {\n", + "- peer-as 65000;\n", + "- }\n", + "- }\n", + "- }\n", + "- }\n", + "- policy-options {\n", + "- policy-statement PERMIT_ALL {\n", + "- from protocol bgp;\n", + "- then accept;\n", + "- }\n", + "- }\n", + "- vlans {\n", + "- backend {\n", + "- vlan-id 200;\n", + "- }\n", + "- frontend {\n", + "- vlan-id 100;\n", + "- }\n", + "- }\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run rollback.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As with other tasks, changes are detected and only when needed are applied:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[34m* spine00.cmh ** changed : False ***********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : False -------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* spine01.cmh ** changed : False ***********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : False -------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* leaf00.cmh ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : False -------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* leaf01.cmh ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : False -------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run rollback.py" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/1_simple_runbooks/rollback.py b/examples/1_simple_runbooks/rollback.py new file mode 100755 index 00000000..655f07ac --- /dev/null +++ b/examples/1_simple_runbooks/rollback.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +""" +Runbook to rollback configuration from a saved configuration +""" +from brigade.easy import easy_brigade +from brigade.plugins.tasks import networking, text + +import click + + +def rollback(task): + """ + This function loads the backup from ./backups/$hostname and + deploys it. + """ + task.run(networking.napalm_configure, + name="Loading Configuration on the device", + replace=True, + filename="backups/{host}") + + +@click.command() +@click.option('--filter', '-f', multiple=True, + help="k=v pairs to filter the devices") +@click.option('--get', '-g', multiple=True, + help="getters you want to use") +def main(filter, get): + brg = easy_brigade( + host_file="../inventory/hosts.yaml", + group_file="../inventory/groups.yaml", + dry_run=False, + raise_on_error=True, + ) + + + # select which devices we want to work with + filtered = brg.filter(type="network_device", site="cmh") + + results = filtered.run(task=rollback) + + filtered.run(text.print_result, + num_workers=1, # task should be done synchronously + data=results, + task_id=-1, # we only want to print the last task + ) +if __name__ == "__main__": + main() diff --git a/examples/1_simple_runbooks/validate.ipynb b/examples/1_simple_runbooks/validate.ipynb new file mode 100644 index 00000000..8af4499e --- /dev/null +++ b/examples/1_simple_runbooks/validate.ipynb @@ -0,0 +1,689 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# ignore this cell, this is just a helper cell to provide the magic %highlight_file\n", + "%run ../highlighter.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Validate\n", + "\n", + "This playbook uses [napalm validation](http://napalm.readthedocs.io/en/latest/validate/index.html) functionality to verify correctness of the network.\n", + "\n", + "## Code" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
 1 #!/usr/bin/env python\n",
+       " 2 """\n",
+       " 3 Runbook that verifies that BGP sessions are configured and up.\n",
+       " 4 """\n",
+       " 5 from brigade.easy import easy_brigade\n",
+       " 6 from brigade.plugins.tasks import data, networking, text\n",
+       " 7 \n",
+       " 8 \n",
+       " 9 def validate(task):\n",
+       "10     task.host["config"] = ""\n",
+       "11 \n",
+       "12     r = task.run(name="read data",\n",
+       "13                  task=data.load_yaml,\n",
+       "14                  file="../extra_data/{host}/l3.yaml")\n",
+       "15 \n",
+       "16     validation_rules = [{\n",
+       "17         'get_bgp_neighbors': {\n",
+       "18             'global': {\n",
+       "19                 'peers': {\n",
+       "20                     '_mode': 'strict',\n",
+       "21                 }\n",
+       "22             }\n",
+       "23         }\n",
+       "24     }]\n",
+       "25     peers = validation_rules[0]['get_bgp_neighbors']['global']['peers']\n",
+       "26     for session in r.result['sessions']:\n",
+       "27         peers[session['ipv4']] = {'is_up': True}\n",
+       "28 \n",
+       "29     task.run(name="validating data",\n",
+       "30              task=networking.napalm_validate,\n",
+       "31              validation_source=validation_rules)\n",
+       "32 \n",
+       "33 \n",
+       "34 def print_compliance(task, results):\n",
+       "35     """\n",
+       "36     We use this task so we can access directly the result\n",
+       "37     for each specific host and see if the task complies or not\n",
+       "38     and pass it to print_result.\n",
+       "39     """\n",
+       "40     task.run(text.print_result,\n",
+       "41              name="print result",\n",
+       "42              data=results[task.host.name],\n",
+       "43              failed=not results[task.host.name][2].result['complies'],\n",
+       "44              )\n",
+       "45 \n",
+       "46 \n",
+       "47 brg = easy_brigade(\n",
+       "48         host_file="../inventory/hosts.yaml",\n",
+       "49         group_file="../inventory/groups.yaml",\n",
+       "50         dry_run=False,\n",
+       "51         raise_on_error=True,\n",
+       "52 )\n",
+       "53 \n",
+       "54 \n",
+       "55 filtered = brg.filter(type="network_device", site="cmh")\n",
+       "56 \n",
+       "57 results = filtered.run(task=validate)\n",
+       "58 \n",
+       "59 filtered.run(print_compliance,\n",
+       "60              results=results,\n",
+       "61              num_workers=1)\n",
+       "
\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%highlight_file validate.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Demo\n", + "\n", + "Let's start by running the script on an unconfigured network:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[31m* spine00.cmh ** changed : False ***********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validate ** changed : False ----------------------------------------------\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- read data ** changed : False ---------------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'interfaces'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'Ethernet1'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'leaf00.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.0.0/31'\u001b[0m}\u001b[0m,\n", + " \u001b[0m'Ethernet2'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'leaf01.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.0.2/31'\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'sessions'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m\u001b[0m{'ipv4': '10.0.0.1', 'peer_as': 65100}\u001b[0m,\n", + " \u001b[0m{'ipv4': '10.0.0.3', 'peer_as': 65101}\u001b[0m]\u001b[0m}\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validating data ** changed : False ---------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'get_bgp_neighbors'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'missing'\u001b[0m: \u001b[0m['global']\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'skipped'\u001b[0m: \u001b[0m[]\u001b[0m}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[31m* spine01.cmh ** changed : False ***********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validate ** changed : False ----------------------------------------------\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- read data ** changed : False ---------------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'interfaces'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'ge-0/0/1'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'leaf00.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.1.0/31'\u001b[0m}\u001b[0m,\n", + " \u001b[0m'ge-0/0/2'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'leaf01.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.1.2/31'\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'sessions'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m\u001b[0m{'ipv4': '10.0.1.1', 'peer_as': 65100}\u001b[0m,\n", + " \u001b[0m{'ipv4': '10.0.1.3', 'peer_as': 65101}\u001b[0m]\u001b[0m}\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validating data ** changed : False ---------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'get_bgp_neighbors'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'missing'\u001b[0m: \u001b[0m['global']\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'skipped'\u001b[0m: \u001b[0m[]\u001b[0m}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[31m* leaf00.cmh ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validate ** changed : False ----------------------------------------------\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- read data ** changed : False ---------------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'interfaces'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'Ethernet1'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'spine00.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.0.1/31'\u001b[0m}\u001b[0m,\n", + " \u001b[0m'Ethernet2'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'spine01.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.1.1/31'\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'sessions'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m\u001b[0m{'ipv4': '10.0.0.0', 'peer_as': 65000}\u001b[0m,\n", + " \u001b[0m{'ipv4': '10.0.1.0', 'peer_as': 65000}\u001b[0m]\u001b[0m}\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validating data ** changed : False ---------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'get_bgp_neighbors'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'missing'\u001b[0m: \u001b[0m['global']\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'skipped'\u001b[0m: \u001b[0m[]\u001b[0m}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[31m* leaf01.cmh ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validate ** changed : False ----------------------------------------------\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- read data ** changed : False ---------------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'interfaces'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'ge-0/0/1'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'spine00.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.0.3/31'\u001b[0m}\u001b[0m,\n", + " \u001b[0m'ge-0/0/2'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'spine01.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.1.3/31'\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'sessions'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m\u001b[0m{'ipv4': '10.0.0.2', 'peer_as': 65000}\u001b[0m,\n", + " \u001b[0m{'ipv4': '10.0.1.2', 'peer_as': 65000}\u001b[0m]\u001b[0m}\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validating data ** changed : False ---------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'get_bgp_neighbors'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'missing'\u001b[0m: \u001b[0m['global']\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'skipped'\u001b[0m: \u001b[0m[]\u001b[0m}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run validate.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For each host we get the data we are using for validation and the result. What the report is saying is that we don't even have the BGP instance 'global' (default instance) configured so let's do it:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[32m**** Playbook to configure the network *****************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* spine00.cmh ** changed : True ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m@@ -8,7 +8,8 @@\n", + " !\n", + " transceiver qsfp default-mode 4x10G\n", + " !\n", + "-hostname blah\n", + "+hostname spine00.cmh\n", + "+ip domain-name cmh.acme.com\n", + " !\n", + " spanning-tree mode mstp\n", + " !\n", + "@@ -20,13 +21,28 @@\n", + " username vagrant privilege 15 role network-admin secret sha512 $6$kRQZJTqx69hOW5ag$Y6VX8Kk37TWEsriKdr6ixqvMuUSSbuFu2Eh/5SIet2TCeXP3bdlwikIAruPp6lHB5HdC.t6tPsZVctHMU7H590\n", + " !\n", + " interface Ethernet1\n", + "+ description link to leaf00.cmh\n", + "+ no switchport\n", + "+ ip address 10.0.0.0/31\n", + " !\n", + " interface Ethernet2\n", + "+ description link to leaf01.cmh\n", + "+ no switchport\n", + "+ ip address 10.0.0.2/31\n", + " !\n", + " interface Management1\n", + " ip address 10.0.2.15/24\n", + " !\n", + "-no ip routing\n", + "+ip routing\n", + "+!\n", + "+router bgp 65000\n", + "+ neighbor 10.0.0.1 remote-as 65100\n", + "+ neighbor 10.0.0.1 maximum-routes 12000 \n", + "+ neighbor 10.0.0.3 remote-as 65101\n", + "+ neighbor 10.0.0.3 maximum-routes 12000 \n", + "+ address-family ipv4\n", + "+ neighbor 10.0.0.1 activate\n", + "+ neighbor 10.0.0.3 activate\n", + " !\n", + " management api http-commands\n", + " no shutdown\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* spine01.cmh ** changed : True ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m[edit system]\n", + "- host-name vsrx;\n", + "+ host-name spine01.cmh;\n", + "+ domain-name cmh.acme.com;\n", + "[edit interfaces]\n", + "+ ge-0/0/1 {\n", + "+ description \"link to leaf00.cmh\";\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ address 10.0.1.0/31;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ ge-0/0/2 {\n", + "+ description \"link to leaf01.cmh\";\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ address 10.0.1.2/31;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "[edit]\n", + "+ routing-options {\n", + "+ autonomous-system 65000;\n", + "+ }\n", + "+ protocols {\n", + "+ bgp {\n", + "+ import PERMIT_ALL;\n", + "+ export PERMIT_ALL;\n", + "+ group peers {\n", + "+ neighbor 10.0.1.1 {\n", + "+ peer-as 65100;\n", + "+ }\n", + "+ neighbor 10.0.1.3 {\n", + "+ peer-as 65101;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ policy-options {\n", + "+ policy-statement PERMIT_ALL {\n", + "+ from protocol bgp;\n", + "+ then accept;\n", + "+ }\n", + "+ }\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* leaf00.cmh ** changed : True *************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m@@ -8,6 +8,9 @@\n", + " !\n", + " transceiver qsfp default-mode 4x10G\n", + " !\n", + "+hostname leaf00.cmh\n", + "+ip domain-name cmh.acme.com\n", + "+!\n", + " spanning-tree mode mstp\n", + " !\n", + " aaa authorization exec default local\n", + "@@ -17,14 +20,36 @@\n", + " username admin privilege 15 role network-admin secret sha512 $6$/K1M3ENrC/xALAOm$1vCB5TfaI8ih5GQRCwhRE7KGzmc.EGuQZ7dEuwhP7AJC0/A97u88miINH/7GtrBpRZ.Inn5JY9tuymMcmyyKc.\n", + " username vagrant privilege 15 role network-admin secret sha512 $6$9CGTCvCiiJK3lDMp$kU9ncPDBkw0w09.h9wIhQtMAkZ/1zD1ds/wlAZAtmSQf5ntNMjDgvmZpBcXWAPAETlk4.kA9niLTVmQwaLBV/.\n", + " !\n", + "+vlan 100\n", + "+ name frontend\n", + "+!\n", + "+vlan 200\n", + "+ name backend\n", + "+!\n", + " interface Ethernet1\n", + "+ description link to spine00.cmh\n", + "+ shutdown\n", + "+ no switchport\n", + "+ ip address 10.0.0.1/31\n", + " !\n", + " interface Ethernet2\n", + "+ description link to spine01.cmh\n", + "+ no switchport\n", + "+ ip address 10.0.1.1/31\n", + " !\n", + " interface Management1\n", + " ip address 10.0.2.15/24\n", + " !\n", + "-no ip routing\n", + "+ip routing\n", + "+!\n", + "+router bgp 65100\n", + "+ neighbor 10.0.0.0 remote-as 65000\n", + "+ neighbor 10.0.0.0 maximum-routes 12000 \n", + "+ neighbor 10.0.1.0 remote-as 65000\n", + "+ neighbor 10.0.1.0 maximum-routes 12000 \n", + "+ address-family ipv4\n", + "+ neighbor 10.0.0.0 activate\n", + "+ neighbor 10.0.1.0 activate\n", + " !\n", + " management api http-commands\n", + " no shutdown\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* leaf01.cmh ** changed : True *************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m[edit system]\n", + "- host-name vsrx;\n", + "+ host-name leaf01.cmh;\n", + "+ domain-name cmh.acme.com;\n", + "[edit interfaces]\n", + "+ ge-0/0/1 {\n", + "+ description \"link to spine00.cmh\";\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ address 10.0.0.3/31;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ ge-0/0/2 {\n", + "+ description \"link to spine01.cmh\";\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ address 10.0.1.3/31;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "[edit]\n", + "+ routing-options {\n", + "+ autonomous-system 65101;\n", + "+ }\n", + "+ protocols {\n", + "+ bgp {\n", + "+ import PERMIT_ALL;\n", + "+ export PERMIT_ALL;\n", + "+ group peers {\n", + "+ neighbor 10.0.0.2 {\n", + "+ peer-as 65000;\n", + "+ }\n", + "+ neighbor 10.0.1.2 {\n", + "+ peer-as 65000;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ policy-options {\n", + "+ policy-statement PERMIT_ALL {\n", + "+ from protocol bgp;\n", + "+ then accept;\n", + "+ }\n", + "+ }\n", + "+ vlans {\n", + "+ backend {\n", + "+ vlan-id 200;\n", + "+ }\n", + "+ frontend {\n", + "+ vlan-id 100;\n", + "+ }\n", + "+ }\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run configure.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now the network is configured let's validate the deployment again:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[31m* spine00.cmh ** changed : False ***********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validate ** changed : False ----------------------------------------------\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- read data ** changed : False ---------------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'interfaces'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'Ethernet1'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'leaf00.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.0.0/31'\u001b[0m}\u001b[0m,\n", + " \u001b[0m'Ethernet2'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'leaf01.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.0.2/31'\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'sessions'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m\u001b[0m{'ipv4': '10.0.0.1', 'peer_as': 65100}\u001b[0m,\n", + " \u001b[0m{'ipv4': '10.0.0.3', 'peer_as': 65101}\u001b[0m]\u001b[0m}\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validating data ** changed : False ---------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'get_bgp_neighbors'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'missing'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'global'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'diff'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'missing'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'peers'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'diff'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m]\u001b[0m,\n", + " \u001b[0m'missing'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m]\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'10.0.0.1'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'diff'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m]\u001b[0m,\n", + " \u001b[0m'missing'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m]\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'is_up'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'actual_value'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'expected_value'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mFalse\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mTrue\u001b[0m}\u001b[0m,\n", + " \u001b[0m'10.0.0.3'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mTrue\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mTrue\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mTrue\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'skipped'\u001b[0m: \u001b[0m[]\u001b[0m}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* spine01.cmh ** changed : False ***********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validate ** changed : False ----------------------------------------------\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- read data ** changed : False ---------------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'interfaces'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'ge-0/0/1'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'leaf00.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.1.0/31'\u001b[0m}\u001b[0m,\n", + " \u001b[0m'ge-0/0/2'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'leaf01.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.1.2/31'\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'sessions'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m\u001b[0m{'ipv4': '10.0.1.1', 'peer_as': 65100}\u001b[0m,\n", + " \u001b[0m{'ipv4': '10.0.1.3', 'peer_as': 65101}\u001b[0m]\u001b[0m}\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validating data ** changed : False ---------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'get_bgp_neighbors'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'missing'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'global'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mTrue\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'skipped'\u001b[0m: \u001b[0m[]\u001b[0m}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[31m* leaf00.cmh ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validate ** changed : False ----------------------------------------------\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- read data ** changed : False ---------------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'interfaces'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'Ethernet1'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'spine00.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.0.1/31'\u001b[0m}\u001b[0m,\n", + " \u001b[0m'Ethernet2'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'spine01.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.1.1/31'\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'sessions'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m\u001b[0m{'ipv4': '10.0.0.0', 'peer_as': 65000}\u001b[0m,\n", + " \u001b[0m{'ipv4': '10.0.1.0', 'peer_as': 65000}\u001b[0m]\u001b[0m}\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validating data ** changed : False ---------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'get_bgp_neighbors'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'missing'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'global'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'diff'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'missing'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'peers'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'diff'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m]\u001b[0m,\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \u001b[0m'missing'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m]\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'10.0.0.0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'diff'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m]\u001b[0m,\n", + " \u001b[0m'missing'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m]\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'is_up'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'actual_value'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'expected_value'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mFalse\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mTrue\u001b[0m}\u001b[0m,\n", + " \u001b[0m'10.0.1.0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mTrue\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mTrue\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mTrue\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'skipped'\u001b[0m: \u001b[0m[]\u001b[0m}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* leaf01.cmh ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validate ** changed : False ----------------------------------------------\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- read data ** changed : False ---------------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'interfaces'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'ge-0/0/1'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'spine00.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.0.3/31'\u001b[0m}\u001b[0m,\n", + " \u001b[0m'ge-0/0/2'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'spine01.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.1.3/31'\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'sessions'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m\u001b[0m{'ipv4': '10.0.0.2', 'peer_as': 65000}\u001b[0m,\n", + " \u001b[0m{'ipv4': '10.0.1.2', 'peer_as': 65000}\u001b[0m]\u001b[0m}\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validating data ** changed : False ---------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'get_bgp_neighbors'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'missing'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'global'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mTrue\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'skipped'\u001b[0m: \u001b[0m[]\u001b[0m}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run validate.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "What the report is basically telling us is that ``spina01`` and ``leaf01`` are pssing our tests, however, ``spine00`` and ``leaf00`` as one of their BGP sessions that should be ``up`` is actually ``down``." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/1_simple_runbooks/validate.py b/examples/1_simple_runbooks/validate.py index 6ef85baf..4453babd 100755 --- a/examples/1_simple_runbooks/validate.py +++ b/examples/1_simple_runbooks/validate.py @@ -1,9 +1,8 @@ #!/usr/bin/env python """ -In this example we write a CLI tool with brigade and click to deploy configuration. +Runbook that verifies that BGP sessions are configured and up. """ -from brigade.core import Brigade -from brigade.plugins.inventory.simple import SimpleInventory +from brigade.easy import easy_brigade from brigade.plugins.tasks import data, networking, text @@ -27,25 +26,33 @@ def validate(task): for session in r.result['sessions']: peers[session['ipv4']] = {'is_up': True} - return task.run(name="validating data", - task=networking.napalm_validate, - validation_source=validation_rules) + task.run(name="validating data", + task=networking.napalm_validate, + validation_source=validation_rules) def print_compliance(task, results): - task.run(name="print result", - task=text.print_result, + """ + We use this task so we can access directly the result + for each specific host and see if the task complies or not + and pass it to print_result. + """ + task.run(text.print_result, + name="print result", data=results[task.host.name], - failed=not results[task.host.name].result['complies'], + failed=not results[task.host.name][2].result['complies'], ) -brigade = Brigade( - inventory=SimpleInventory("../hosts.yaml", "../groups.yaml"), - dry_run=False, +brg = easy_brigade( + host_file="../inventory/hosts.yaml", + group_file="../inventory/groups.yaml", + dry_run=False, + raise_on_error=True, ) -filtered = brigade.filter(type="network_device", site="cmh") + +filtered = brg.filter(type="network_device", site="cmh") results = filtered.run(task=validate) diff --git a/examples/2_simple_tooling/backup.ipynb b/examples/2_simple_tooling/backup.ipynb new file mode 100644 index 00000000..7df81f0d --- /dev/null +++ b/examples/2_simple_tooling/backup.ipynb @@ -0,0 +1,1062 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# ignore this cell, this is just a helper cell to provide the magic %highlight_file\n", + "%run ../highlighter.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Backup\n", + "\n", + "## Code" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
 1 #!/usr/bin/env python\n",
+       " 2 """\n",
+       " 3 Tool that downloads the configuration from the devices and\n",
+       " 4 stores them on disk.\n",
+       " 5 """\n",
+       " 6 from brigade.easy import easy_brigade\n",
+       " 7 from brigade.plugins.tasks import files, networking, text\n",
+       " 8 \n",
+       " 9 import click\n",
+       "10 \n",
+       "11 \n",
+       "12 def backup(task, path):\n",
+       "13     """\n",
+       "14     This function groups two tasks:\n",
+       "15         1. Download configuration from the device\n",
+       "16         2. Store to disk\n",
+       "17     """\n",
+       "18     result = task.run(networking.napalm_get,\n",
+       "19                       name="Gathering configuration from the device",\n",
+       "20                       getters="config")\n",
+       "21 \n",
+       "22     task.run(files.write,\n",
+       "23              name="Saving Configuration to disk",\n",
+       "24              content=result.result["config"]["running"],\n",
+       "25              filename="{}/{}".format(path, task.host))\n",
+       "26 \n",
+       "27 \n",
+       "28 @click.command()\n",
+       "29 @click.option('--filter', '-f', multiple=True,\n",
+       "30               help="filters to apply. For instance site=cmh")\n",
+       "31 @click.option('--path', '-p', default=".",\n",
+       "32               help="Where to save the backup files")\n",
+       "33 def main(filter, path):\n",
+       "34     """\n",
+       "35     Backups running configuration of devices into a file\n",
+       "36     """\n",
+       "37     brg = easy_brigade(\n",
+       "38             host_file="../inventory/hosts.yaml",\n",
+       "39             group_file="../inventory/groups.yaml",\n",
+       "40             dry_run=False,\n",
+       "41             raise_on_error=False,\n",
+       "42     )\n",
+       "43 \n",
+       "44     # filter is going to be a list of key=value so we clean that first\n",
+       "45     filter_dict = {"type": "network_device"}\n",
+       "46     for f in filter:\n",
+       "47         k, v = f.split("=")\n",
+       "48         filter_dict[k] = v\n",
+       "49 \n",
+       "50     # let's filter the devices\n",
+       "51     filtered = brg.filter(**filter_dict)\n",
+       "52 \n",
+       "53     # Run the ``backup`` function that groups the tasks to\n",
+       "54     # download/store devices' configuration\n",
+       "55     results = filtered.run(backup,\n",
+       "56                            name="Backing up configurations",\n",
+       "57                            path=path)\n",
+       "58 \n",
+       "59     # Let's print the result on screen\n",
+       "60     filtered.run(text.print_result,\n",
+       "61                  num_workers=1,  # task should be done synchronously\n",
+       "62                  data=results,\n",
+       "63                  task_id=-1,  # we only want to print the last task\n",
+       "64                  skipped=True,\n",
+       "65                  )\n",
+       "66 \n",
+       "67 \n",
+       "68 if __name__ == "__main__":\n",
+       "69     main()\n",
+       "
\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%highlight_file backup.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Demo\n", + "\n", + "Let's start with the help so we can see what we can do." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Usage: backup.py [OPTIONS]\n", + "\n", + " Backups running configuration of devices into a file\n", + "\n", + "Options:\n", + " -f, --filter TEXT filters to apply. For instance site=cmh\n", + " -p, --path TEXT Where to save the backup files\n", + " --help Show this message and exit.\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run backup.py --help" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With those options it should be easy to backup devices at different sites in different paths:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "mkdir: backups/cmh: File exists\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[33m* spine00.cmh ** changed : True ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Saving Configuration to disk ** changed : True ---------------------------\u001b[0m\n", + "\u001b[0m--- backups/cmh//spine00.cmh\n", + "\n", + "+++ new\n", + "\n", + "@@ -0,0 +1,52 @@\n", + "\n", + "+! Command: show running-config\n", + "+! device: spine00.cmh (vEOS, EOS-4.17.5M)\n", + "+!\n", + "+! boot system flash:/vEOS-lab.swi\n", + "+!\n", + "+event-handler dhclient\n", + "+ trigger on-boot\n", + "+ action bash sudo /mnt/flash/initialize_ma1.sh\n", + "+!\n", + "+transceiver qsfp default-mode 4x10G\n", + "+!\n", + "+hostname spine00.cmh\n", + "+ip domain-name cmh.acme.com\n", + "+!\n", + "+spanning-tree mode mstp\n", + "+!\n", + "+aaa authorization exec default local\n", + "+!\n", + "+aaa root secret sha512 $6$5stn7z2imBLV6iO0$w0ZnOhy8SwNdELdO2da9q8wDKerYTyY8evY052UoyRJ2Wo6liaUneuTFGphL8JQD9gtESOipCBb6PYmSMuUjs.\n", + "+!\n", + "+username admin privilege 15 role network-admin secret sha512 $6$qkXlQpatVlanYe9v$aHTbPaGTaqDRCp5WSC3DPpDfblYSE24.OHeKgGOOTf0.Ol2lDpivTvHByx5tU41sVOGcHqc4U4LgrKv8AjbKQ/\n", + "+username vagrant privilege 15 role network-admin secret sha512 $6$kRQZJTqx69hOW5ag$Y6VX8Kk37TWEsriKdr6ixqvMuUSSbuFu2Eh/5SIet2TCeXP3bdlwikIAruPp6lHB5HdC.t6tPsZVctHMU7H590\n", + "+!\n", + "+interface Ethernet1\n", + "+ description link to leaf00.cmh\n", + "+ no switchport\n", + "+ ip address 10.0.0.0/31\n", + "+!\n", + "+interface Ethernet2\n", + "+ description link to leaf01.cmh\n", + "+ no switchport\n", + "+ ip address 10.0.0.2/31\n", + "+!\n", + "+interface Management1\n", + "+ ip address 10.0.2.15/24\n", + "+!\n", + "+ip routing\n", + "+!\n", + "+router bgp 65000\n", + "+ neighbor 10.0.0.1 remote-as 65100\n", + "+ neighbor 10.0.0.1 maximum-routes 12000 \n", + "+ neighbor 10.0.0.3 remote-as 65101\n", + "+ neighbor 10.0.0.3 maximum-routes 12000 \n", + "+ address-family ipv4\n", + "+ neighbor 10.0.0.1 activate\n", + "+ neighbor 10.0.0.3 activate\n", + "+!\n", + "+management api http-commands\n", + "+ no shutdown\n", + "+!\n", + "+!\n", + "+end\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* spine01.cmh ** changed : True ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Saving Configuration to disk ** changed : True ---------------------------\u001b[0m\n", + "\u001b[0m--- backups/cmh//spine01.cmh\n", + "\n", + "+++ new\n", + "\n", + "@@ -0,0 +1,110 @@\n", + "\n", + "+\n", + "+## Last commit: 2018-01-15 12:02:22 UTC by vagrant\n", + "+version 12.1X47-D20.7;\n", + "+system {\n", + "+ host-name spine01.cmh;\n", + "+ domain-name cmh.acme.com;\n", + "+ root-authentication {\n", + "+ encrypted-password \"$1$5MhDFyrI$NBBMndW1POqbN.0QEA4z0.\";\n", + "+ ssh-rsa \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDi/i8iiAZsXC5qdmJZpTxKjUyyoMgEGoHXl/TMFdJjSV+XAZ18OXAEsvPO0AlXJ6RZTwK8Zcr6TLq4l1Kssd+kVN02shFkgDo3wWf3I2BXKKdog6/6fbhiD1SgCeafzWBlUQvREgDQDy1XSFjNjSJ39vtOa8ikqGdbf4XH0hjoLHYDV0H0VNZLboULCNFPF0PHQfPrsp2AXHU+p7sl61GhZgfw6WuLIzXWqJyq9B0Q5XgdmvnvdjZeTOShoPTPbaRYVVFOMGTqJQOZsl5P3wTIJT8JG7iEz1Tiar8nmltON83sy/lEODhZkJPXe3zw3fwUIS9yQ53z0t1UGHm7KGNX vagrant\";\n", + "+ }\n", + "+ login {\n", + "+ user vagrant {\n", + "+ uid 2000;\n", + "+ class super-user;\n", + "+ authentication {\n", + "+ ssh-rsa \"ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key\";\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ services {\n", + "+ ssh {\n", + "+ root-login allow;\n", + "+ }\n", + "+ netconf {\n", + "+ ssh;\n", + "+ }\n", + "+ web-management {\n", + "+ http {\n", + "+ interface ge-0/0/0.0;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ syslog {\n", + "+ user * {\n", + "+ any emergency;\n", + "+ }\n", + "+ file messages {\n", + "+ any any;\n", + "+ authorization info;\n", + "+ }\n", + "+ file interactive-commands {\n", + "+ interactive-commands any;\n", + "+ }\n", + "+ }\n", + "+ license {\n", + "+ autoupdate {\n", + "+ url https://ae1.juniper.net/junos/key_retrieval;\n", + "+ }\n", + "+ }\n", + "+}\n", + "+interfaces {\n", + "+ ge-0/0/0 {\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ dhcp;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ ge-0/0/1 {\n", + "+ description \"link to leaf00.cmh\";\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ address 10.0.1.0/31;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ ge-0/0/2 {\n", + "+ description \"link to leaf01.cmh\";\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ address 10.0.1.2/31;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+}\n", + "+routing-options {\n", + "+ autonomous-system 65000;\n", + "+}\n", + "+protocols {\n", + "+ bgp {\n", + "+ import PERMIT_ALL;\n", + "+ export PERMIT_ALL;\n", + "+ group peers {\n", + "+ neighbor 10.0.1.1 {\n", + "+ peer-as 65100;\n", + "+ }\n", + "+ neighbor 10.0.1.3 {\n", + "+ peer-as 65101;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+}\n", + "+policy-options {\n", + "+ policy-statement PERMIT_ALL {\n", + "+ from protocol bgp;\n", + "+ then accept;\n", + "+ }\n", + "+}\n", + "+security {\n", + "+ forwarding-options {\n", + "+ family {\n", + "+ inet6 {\n", + "+ mode packet-based;\n", + "+ }\n", + "+ mpls {\n", + "+ mode packet-based;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* leaf00.cmh ** changed : True *************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Saving Configuration to disk ** changed : True ---------------------------\u001b[0m\n", + "\u001b[0m--- backups/cmh//leaf00.cmh\n", + "\n", + "+++ new\n", + "\n", + "@@ -0,0 +1,59 @@\n", + "\n", + "+! Command: show running-config\n", + "+! device: leaf00.cmh (vEOS, EOS-4.17.5M)\n", + "+!\n", + "+! boot system flash:/vEOS-lab.swi\n", + "+!\n", + "+event-handler dhclient\n", + "+ trigger on-boot\n", + "+ action bash sudo /mnt/flash/initialize_ma1.sh\n", + "+!\n", + "+transceiver qsfp default-mode 4x10G\n", + "+!\n", + "+hostname leaf00.cmh\n", + "+ip domain-name cmh.acme.com\n", + "+!\n", + "+spanning-tree mode mstp\n", + "+!\n", + "+aaa authorization exec default local\n", + "+!\n", + "+aaa root secret sha512 $6$sRifRAo/DXihW7sG$3r4MMTsslNCCWdD/FFIw3lvnnkI4SWO0bvhEzvWSurrOBgUsxjrmgN5kywH5Ta7LNNXiWjFfjwoyefn9nqeB2/\n", + "+!\n", + "+username admin privilege 15 role network-admin secret sha512 $6$/K1M3ENrC/xALAOm$1vCB5TfaI8ih5GQRCwhRE7KGzmc.EGuQZ7dEuwhP7AJC0/A97u88miINH/7GtrBpRZ.Inn5JY9tuymMcmyyKc.\n", + "+username vagrant privilege 15 role network-admin secret sha512 $6$9CGTCvCiiJK3lDMp$kU9ncPDBkw0w09.h9wIhQtMAkZ/1zD1ds/wlAZAtmSQf5ntNMjDgvmZpBcXWAPAETlk4.kA9niLTVmQwaLBV/.\n", + "+!\n", + "+vlan 100\n", + "+ name frontend\n", + "+!\n", + "+vlan 200\n", + "+ name backend\n", + "+!\n", + "+interface Ethernet1\n", + "+ description link to spine00.cmh\n", + "+ shutdown\n", + "+ no switchport\n", + "+ ip address 10.0.0.1/31\n", + "+!\n", + "+interface Ethernet2\n", + "+ description link to spine01.cmh\n", + "+ no switchport\n", + "+ ip address 10.0.1.1/31\n", + "+!\n", + "+interface Management1\n", + "+ ip address 10.0.2.15/24\n", + "+!\n", + "+ip routing\n", + "+!\n", + "+router bgp 65100\n", + "+ neighbor 10.0.0.0 remote-as 65000\n", + "+ neighbor 10.0.0.0 maximum-routes 12000 \n", + "+ neighbor 10.0.1.0 remote-as 65000\n", + "+ neighbor 10.0.1.0 maximum-routes 12000 \n", + "+ address-family ipv4\n", + "+ neighbor 10.0.0.0 activate\n", + "+ neighbor 10.0.1.0 activate\n", + "+!\n", + "+management api http-commands\n", + "+ no shutdown\n", + "+!\n", + "+!\n", + "+end\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* leaf01.cmh ** changed : True *************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Saving Configuration to disk ** changed : True ---------------------------\u001b[0m\n", + "\u001b[0m--- backups/cmh//leaf01.cmh\n", + "\n", + "+++ new\n", + "\n", + "@@ -0,0 +1,118 @@\n", + "\n", + "+\n", + "+## Last commit: 2018-01-15 12:02:23 UTC by vagrant\n", + "+version 12.1X47-D20.7;\n", + "+system {\n", + "+ host-name leaf01.cmh;\n", + "+ domain-name cmh.acme.com;\n", + "+ root-authentication {\n", + "+ encrypted-password \"$1$5MhDFyrI$NBBMndW1POqbN.0QEA4z0.\";\n", + "+ ssh-rsa \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDsfGpEhGi8CbjIHJkMju/CJH6IuQiIzZyDt+AVieDfXKWDuBSOfc7YV8xNdYMqQqpDOWmEVZ7dhfD6IWDI3aa6WLkEXORD+zScjQo+5iHty6VlI61ImHQkWhWX6pZi3Cq/JsH8oldIC2xvzFNWB2p1suu+rzuGtJjbDq5NMlp1bNSiBgV0dHZR6Lt1UuK/rVBl7FbBN8HpInM+a37SkkwIrKMK8z42Ax9ufd17P3SqZP8oo+Ql4Y3aeCz2t4CfZNh9YRLZSiUYF16VN+31mzKEqT7+0rFlyfv/CaPwyfAv2BPFljUEsyFsWU923EGYQsfOIKVnd+zzHDHIHapVMQbh vagrant\";\n", + "+ }\n", + "+ login {\n", + "+ user vagrant {\n", + "+ uid 2000;\n", + "+ class super-user;\n", + "+ authentication {\n", + "+ ssh-rsa \"ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key\";\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ services {\n", + "+ ssh {\n", + "+ root-login allow;\n", + "+ }\n", + "+ netconf {\n", + "+ ssh;\n", + "+ }\n", + "+ web-management {\n", + "+ http {\n", + "+ interface ge-0/0/0.0;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ syslog {\n", + "+ user * {\n", + "+ any emergency;\n", + "+ }\n", + "+ file messages {\n", + "+ any any;\n", + "+ authorization info;\n", + "+ }\n", + "+ file interactive-commands {\n", + "+ interactive-commands any;\n", + "+ }\n", + "+ }\n", + "+ license {\n", + "+ autoupdate {\n", + "+ url https://ae1.juniper.net/junos/key_retrieval;\n", + "+ }\n", + "+ }\n", + "+}\n", + "+interfaces {\n", + "+ ge-0/0/0 {\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ dhcp;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ ge-0/0/1 {\n", + "+ description \"link to spine00.cmh\";\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ address 10.0.0.3/31;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ ge-0/0/2 {\n", + "+ description \"link to spine01.cmh\";\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ address 10.0.1.3/31;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+}\n", + "+routing-options {\n", + "+ autonomous-system 65101;\n", + "+}\n", + "+protocols {\n", + "+ bgp {\n", + "+ import PERMIT_ALL;\n", + "+ export PERMIT_ALL;\n", + "+ group peers {\n", + "+ neighbor 10.0.0.2 {\n", + "+ peer-as 65000;\n", + "+ }\n", + "+ neighbor 10.0.1.2 {\n", + "+ peer-as 65000;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+}\n", + "+policy-options {\n", + "+ policy-statement PERMIT_ALL {\n", + "+ from protocol bgp;\n", + "+ then accept;\n", + "+ }\n", + "+}\n", + "+security {\n", + "+ forwarding-options {\n", + "+ family {\n", + "+ inet6 {\n", + "+ mode packet-based;\n", + "+ }\n", + "+ mpls {\n", + "+ mode packet-based;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+}\n", + "+vlans {\n", + "+ backend {\n", + "+ vlan-id 200;\n", + "+ }\n", + "+ frontend {\n", + "+ vlan-id 100;\n", + "+ }\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "+}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%mkdir backups/cmh\n", + "%rm backups/cmh/*\n", + "%run backup.py --filter site=cmh --path backups/cmh/" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "mkdir: backups/bma: File exists\n", + "\u001b[0m\u001b[0m\u001b[0m\u001b[0m\u001b[1m\u001b[33m* spine00.bma ** changed : True ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Saving Configuration to disk ** changed : True ---------------------------\u001b[0m\n", + "\u001b[0m--- backups/bma//spine00.bma\n", + "\n", + "+++ new\n", + "\n", + "@@ -0,0 +1,52 @@\n", + "\n", + "+! Command: show running-config\n", + "+! device: spine00.cmh (vEOS, EOS-4.17.5M)\n", + "+!\n", + "+! boot system flash:/vEOS-lab.swi\n", + "+!\n", + "+event-handler dhclient\n", + "+ trigger on-boot\n", + "+ action bash sudo /mnt/flash/initialize_ma1.sh\n", + "+!\n", + "+transceiver qsfp default-mode 4x10G\n", + "+!\n", + "+hostname spine00.cmh\n", + "+ip domain-name cmh.acme.com\n", + "+!\n", + "+spanning-tree mode mstp\n", + "+!\n", + "+aaa authorization exec default local\n", + "+!\n", + "+aaa root secret sha512 $6$5stn7z2imBLV6iO0$w0ZnOhy8SwNdELdO2da9q8wDKerYTyY8evY052UoyRJ2Wo6liaUneuTFGphL8JQD9gtESOipCBb6PYmSMuUjs.\n", + "+!\n", + "+username admin privilege 15 role network-admin secret sha512 $6$qkXlQpatVlanYe9v$aHTbPaGTaqDRCp5WSC3DPpDfblYSE24.OHeKgGOOTf0.Ol2lDpivTvHByx5tU41sVOGcHqc4U4LgrKv8AjbKQ/\n", + "+username vagrant privilege 15 role network-admin secret sha512 $6$kRQZJTqx69hOW5ag$Y6VX8Kk37TWEsriKdr6ixqvMuUSSbuFu2Eh/5SIet2TCeXP3bdlwikIAruPp6lHB5HdC.t6tPsZVctHMU7H590\n", + "+!\n", + "+interface Ethernet1\n", + "+ description link to leaf00.cmh\n", + "+ no switchport\n", + "+ ip address 10.0.0.0/31\n", + "+!\n", + "+interface Ethernet2\n", + "+ description link to leaf01.cmh\n", + "+ no switchport\n", + "+ ip address 10.0.0.2/31\n", + "+!\n", + "+interface Management1\n", + "+ ip address 10.0.2.15/24\n", + "+!\n", + "+ip routing\n", + "+!\n", + "+router bgp 65000\n", + "+ neighbor 10.0.0.1 remote-as 65100\n", + "+ neighbor 10.0.0.1 maximum-routes 12000 \n", + "+ neighbor 10.0.0.3 remote-as 65101\n", + "+ neighbor 10.0.0.3 maximum-routes 12000 \n", + "+ address-family ipv4\n", + "+ neighbor 10.0.0.1 activate\n", + "+ neighbor 10.0.0.3 activate\n", + "+!\n", + "+management api http-commands\n", + "+ no shutdown\n", + "+!\n", + "+!\n", + "+end\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* spine01.bma ** changed : True ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Saving Configuration to disk ** changed : True ---------------------------\u001b[0m\n", + "\u001b[0m--- backups/bma//spine01.bma\n", + "\n", + "+++ new\n", + "\n", + "@@ -0,0 +1,110 @@\n", + "\n", + "+\n", + "+## Last commit: 2018-01-15 12:02:22 UTC by vagrant\n", + "+version 12.1X47-D20.7;\n", + "+system {\n", + "+ host-name spine01.cmh;\n", + "+ domain-name cmh.acme.com;\n", + "+ root-authentication {\n", + "+ encrypted-password \"$1$5MhDFyrI$NBBMndW1POqbN.0QEA4z0.\";\n", + "+ ssh-rsa \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDi/i8iiAZsXC5qdmJZpTxKjUyyoMgEGoHXl/TMFdJjSV+XAZ18OXAEsvPO0AlXJ6RZTwK8Zcr6TLq4l1Kssd+kVN02shFkgDo3wWf3I2BXKKdog6/6fbhiD1SgCeafzWBlUQvREgDQDy1XSFjNjSJ39vtOa8ikqGdbf4XH0hjoLHYDV0H0VNZLboULCNFPF0PHQfPrsp2AXHU+p7sl61GhZgfw6WuLIzXWqJyq9B0Q5XgdmvnvdjZeTOShoPTPbaRYVVFOMGTqJQOZsl5P3wTIJT8JG7iEz1Tiar8nmltON83sy/lEODhZkJPXe3zw3fwUIS9yQ53z0t1UGHm7KGNX vagrant\";\n", + "+ }\n", + "+ login {\n", + "+ user vagrant {\n", + "+ uid 2000;\n", + "+ class super-user;\n", + "+ authentication {\n", + "+ ssh-rsa \"ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key\";\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ services {\n", + "+ ssh {\n", + "+ root-login allow;\n", + "+ }\n", + "+ netconf {\n", + "+ ssh;\n", + "+ }\n", + "+ web-management {\n", + "+ http {\n", + "+ interface ge-0/0/0.0;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ syslog {\n", + "+ user * {\n", + "+ any emergency;\n", + "+ }\n", + "+ file messages {\n", + "+ any any;\n", + "+ authorization info;\n", + "+ }\n", + "+ file interactive-commands {\n", + "+ interactive-commands any;\n", + "+ }\n", + "+ }\n", + "+ license {\n", + "+ autoupdate {\n", + "+ url https://ae1.juniper.net/junos/key_retrieval;\n", + "+ }\n", + "+ }\n", + "+}\n", + "+interfaces {\n", + "+ ge-0/0/0 {\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ dhcp;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ ge-0/0/1 {\n", + "+ description \"link to leaf00.cmh\";\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ address 10.0.1.0/31;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ ge-0/0/2 {\n", + "+ description \"link to leaf01.cmh\";\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ address 10.0.1.2/31;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+}\n", + "+routing-options {\n", + "+ autonomous-system 65000;\n", + "+}\n", + "+protocols {\n", + "+ bgp {\n", + "+ import PERMIT_ALL;\n", + "+ export PERMIT_ALL;\n", + "+ group peers {\n", + "+ neighbor 10.0.1.1 {\n", + "+ peer-as 65100;\n", + "+ }\n", + "+ neighbor 10.0.1.3 {\n", + "+ peer-as 65101;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+}\n", + "+policy-options {\n", + "+ policy-statement PERMIT_ALL {\n", + "+ from protocol bgp;\n", + "+ then accept;\n", + "+ }\n", + "+}\n", + "+security {\n", + "+ forwarding-options {\n", + "+ family {\n", + "+ inet6 {\n", + "+ mode packet-based;\n", + "+ }\n", + "+ mpls {\n", + "+ mode packet-based;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* leaf00.bma ** changed : True *************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Saving Configuration to disk ** changed : True ---------------------------\u001b[0m\n", + "\u001b[0m--- backups/bma//leaf00.bma\n", + "\n", + "+++ new\n", + "\n", + "@@ -0,0 +1,59 @@\n", + "\n", + "+! Command: show running-config\n", + "+! device: leaf00.cmh (vEOS, EOS-4.17.5M)\n", + "+!\n", + "+! boot system flash:/vEOS-lab.swi\n", + "+!\n", + "+event-handler dhclient\n", + "+ trigger on-boot\n", + "+ action bash sudo /mnt/flash/initialize_ma1.sh\n", + "+!\n", + "+transceiver qsfp default-mode 4x10G\n", + "+!\n", + "+hostname leaf00.cmh\n", + "+ip domain-name cmh.acme.com\n", + "+!\n", + "+spanning-tree mode mstp\n", + "+!\n", + "+aaa authorization exec default local\n", + "+!\n", + "+aaa root secret sha512 $6$sRifRAo/DXihW7sG$3r4MMTsslNCCWdD/FFIw3lvnnkI4SWO0bvhEzvWSurrOBgUsxjrmgN5kywH5Ta7LNNXiWjFfjwoyefn9nqeB2/\n", + "+!\n", + "+username admin privilege 15 role network-admin secret sha512 $6$/K1M3ENrC/xALAOm$1vCB5TfaI8ih5GQRCwhRE7KGzmc.EGuQZ7dEuwhP7AJC0/A97u88miINH/7GtrBpRZ.Inn5JY9tuymMcmyyKc.\n", + "+username vagrant privilege 15 role network-admin secret sha512 $6$9CGTCvCiiJK3lDMp$kU9ncPDBkw0w09.h9wIhQtMAkZ/1zD1ds/wlAZAtmSQf5ntNMjDgvmZpBcXWAPAETlk4.kA9niLTVmQwaLBV/.\n", + "+!\n", + "+vlan 100\n", + "+ name frontend\n", + "+!\n", + "+vlan 200\n", + "+ name backend\n", + "+!\n", + "+interface Ethernet1\n", + "+ description link to spine00.cmh\n", + "+ shutdown\n", + "+ no switchport\n", + "+ ip address 10.0.0.1/31\n", + "+!\n", + "+interface Ethernet2\n", + "+ description link to spine01.cmh\n", + "+ no switchport\n", + "+ ip address 10.0.1.1/31\n", + "+!\n", + "+interface Management1\n", + "+ ip address 10.0.2.15/24\n", + "+!\n", + "+ip routing\n", + "+!\n", + "+router bgp 65100\n", + "+ neighbor 10.0.0.0 remote-as 65000\n", + "+ neighbor 10.0.0.0 maximum-routes 12000 \n", + "+ neighbor 10.0.1.0 remote-as 65000\n", + "+ neighbor 10.0.1.0 maximum-routes 12000 \n", + "+ address-family ipv4\n", + "+ neighbor 10.0.0.0 activate\n", + "+ neighbor 10.0.1.0 activate\n", + "+!\n", + "+management api http-commands\n", + "+ no shutdown\n", + "+!\n", + "+!\n", + "+end\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[31m* leaf01.bma ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Backing up configurations ** changed : False -----------------------------\u001b[0m\n", + "\u001b[0mTraceback (most recent call last):\n", + " File \"/Users/dbarroso/.virtualenvs/brigade/lib/python3.6/site-packages/jnpr/junos/device.py\", line 1250, in open\n", + " device_params={'name': 'junos', 'local': False})\n", + " File \"/Users/dbarroso/.virtualenvs/brigade/lib/python3.6/site-packages/ncclient/manager.py\", line 154, in connect\n", + " return connect_ssh(*args, **kwds)\n", + " File \"/Users/dbarroso/.virtualenvs/brigade/lib/python3.6/site-packages/ncclient/manager.py\", line 119, in connect_ssh\n", + " session.connect(*args, **kwds)\n", + " File \"/Users/dbarroso/.virtualenvs/brigade/lib/python3.6/site-packages/ncclient/transport/ssh.py\", line 412, in connect\n", + " self._auth(username, password, key_filenames, allow_agent, look_for_keys)\n", + " File \"/Users/dbarroso/.virtualenvs/brigade/lib/python3.6/site-packages/ncclient/transport/ssh.py\", line 508, in _auth\n", + " raise AuthenticationError(repr(saved_exception))\n", + "ncclient.transport.errors.AuthenticationError: AuthenticationException('Authentication failed.',)\n", + "\n", + "During handling of the above exception, another exception occurred:\n", + "\n", + "Traceback (most recent call last):\n", + " File \"/Users/dbarroso/workspace/brigade/brigade/core/__init__.py\", line 201, in run_task\n", + " r = task._start(host=host, brigade=brigade, dry_run=dry_run)\n", + " File \"/Users/dbarroso/workspace/brigade/brigade/core/task.py\", line 42, in _start\n", + " r = self.task(self, **self.params) or Result(host)\n", + " File \"/Users/dbarroso/workspace/brigade/examples/2_simple_tooling/backup.py\", line 20, in backup\n", + " getters=\"config\")\n", + " File \"/Users/dbarroso/workspace/brigade/brigade/core/task.py\", line 67, in run\n", + " r = Task(task, **kwargs)._start(self.host, self.brigade, dry_run, sub_task=True)\n", + " File \"/Users/dbarroso/workspace/brigade/brigade/core/task.py\", line 42, in _start\n", + " r = self.task(self, **self.params) or Result(host)\n", + " File \"/Users/dbarroso/workspace/brigade/brigade/plugins/tasks/networking/napalm_get.py\", line 16, in napalm_get\n", + " device = task.host.get_connection(\"napalm\")\n", + " File \"/Users/dbarroso/workspace/brigade/brigade/core/inventory.py\", line 212, in get_connection\n", + " raise r[self.name].exception\n", + " File \"/Users/dbarroso/workspace/brigade/brigade/core/__init__.py\", line 201, in run_task\n", + " r = task._start(host=host, brigade=brigade, dry_run=dry_run)\n", + " File \"/Users/dbarroso/workspace/brigade/brigade/core/task.py\", line 42, in _start\n", + " r = self.task(self, **self.params) or Result(host)\n", + " File \"/Users/dbarroso/workspace/brigade/brigade/plugins/tasks/connections/napalm_connection.py\", line 31, in napalm_connection\n", + " host.connections[\"napalm\"].open()\n", + " File \"/Users/dbarroso/.virtualenvs/brigade/lib/python3.6/site-packages/napalm/junos/junos.py\", line 106, in open\n", + " self.device.open()\n", + " File \"/Users/dbarroso/.virtualenvs/brigade/lib/python3.6/site-packages/jnpr/junos/device.py\", line 1254, in open\n", + " raise EzErrors.ConnectAuthError(self)\n", + "jnpr.junos.exception.ConnectAuthError: ConnectAuthError(127.0.0.1)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%mkdir backups/bma\n", + "%rm backups/bma/*\n", + "%run backup.py --filter site=bma --path backups/bma/" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[31m* leaf01.bma ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Backing up configurations ** changed : False -----------------------------\u001b[0m\n", + "\u001b[0mTraceback (most recent call last):\n", + " File \"/Users/dbarroso/.virtualenvs/brigade/lib/python3.6/site-packages/jnpr/junos/device.py\", line 1250, in open\n", + " device_params={'name': 'junos', 'local': False})\n", + " File \"/Users/dbarroso/.virtualenvs/brigade/lib/python3.6/site-packages/ncclient/manager.py\", line 154, in connect\n", + " return connect_ssh(*args, **kwds)\n", + " File \"/Users/dbarroso/.virtualenvs/brigade/lib/python3.6/site-packages/ncclient/manager.py\", line 119, in connect_ssh\n", + " session.connect(*args, **kwds)\n", + " File \"/Users/dbarroso/.virtualenvs/brigade/lib/python3.6/site-packages/ncclient/transport/ssh.py\", line 412, in connect\n", + " self._auth(username, password, key_filenames, allow_agent, look_for_keys)\n", + " File \"/Users/dbarroso/.virtualenvs/brigade/lib/python3.6/site-packages/ncclient/transport/ssh.py\", line 508, in _auth\n", + " raise AuthenticationError(repr(saved_exception))\n", + "ncclient.transport.errors.AuthenticationError: AuthenticationException('Authentication failed.',)\n", + "\n", + "During handling of the above exception, another exception occurred:\n", + "\n", + "Traceback (most recent call last):\n", + " File \"/Users/dbarroso/workspace/brigade/brigade/core/__init__.py\", line 201, in run_task\n", + " r = task._start(host=host, brigade=brigade, dry_run=dry_run)\n", + " File \"/Users/dbarroso/workspace/brigade/brigade/core/task.py\", line 42, in _start\n", + " r = self.task(self, **self.params) or Result(host)\n", + " File \"/Users/dbarroso/workspace/brigade/examples/2_simple_tooling/backup.py\", line 20, in backup\n", + " getters=\"config\")\n", + " File \"/Users/dbarroso/workspace/brigade/brigade/core/task.py\", line 67, in run\n", + " r = Task(task, **kwargs)._start(self.host, self.brigade, dry_run, sub_task=True)\n", + " File \"/Users/dbarroso/workspace/brigade/brigade/core/task.py\", line 42, in _start\n", + " r = self.task(self, **self.params) or Result(host)\n", + " File \"/Users/dbarroso/workspace/brigade/brigade/plugins/tasks/networking/napalm_get.py\", line 16, in napalm_get\n", + " device = task.host.get_connection(\"napalm\")\n", + " File \"/Users/dbarroso/workspace/brigade/brigade/core/inventory.py\", line 212, in get_connection\n", + " raise r[self.name].exception\n", + " File \"/Users/dbarroso/workspace/brigade/brigade/core/__init__.py\", line 201, in run_task\n", + " r = task._start(host=host, brigade=brigade, dry_run=dry_run)\n", + " File \"/Users/dbarroso/workspace/brigade/brigade/core/task.py\", line 42, in _start\n", + " r = self.task(self, **self.params) or Result(host)\n", + " File \"/Users/dbarroso/workspace/brigade/brigade/plugins/tasks/connections/napalm_connection.py\", line 31, in napalm_connection\n", + " host.connections[\"napalm\"].open()\n", + " File \"/Users/dbarroso/.virtualenvs/brigade/lib/python3.6/site-packages/napalm/junos/junos.py\", line 106, in open\n", + " self.device.open()\n", + " File \"/Users/dbarroso/.virtualenvs/brigade/lib/python3.6/site-packages/jnpr/junos/device.py\", line 1254, in open\n", + " raise EzErrors.ConnectAuthError(self)\n", + "jnpr.junos.exception.ConnectAuthError: ConnectAuthError(127.0.0.1)\n", + "\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run backup.py --filter name=leaf01.bma --path backups/bma/" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> Note that brigade detected and reported that we failed to authenticate to one of the devices.\n", + "\n", + "Now we can check we have the backups in the right place:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "backups/bma:\n", + "leaf00.bma spine00.bma spine01.bma\n", + "\n", + "backups/cmh:\n", + "leaf00.cmh leaf01.cmh spine00.cmh spine01.cmh\n", + "\u001b[0m\u001b[0m" + ] + } + ], + "source": [ + "% ls backups/*" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/2_simple_tooling/backup.py b/examples/2_simple_tooling/backup.py index bf68c168..ba3d79b3 100755 --- a/examples/2_simple_tooling/backup.py +++ b/examples/2_simple_tooling/backup.py @@ -1,40 +1,44 @@ #!/usr/bin/env python """ -This is a simple example where we use click and brigade to build a simple CLI tool to retrieve -hosts information. - -The main difference with get_facts_simple.py is that instead of calling a plugin directly -we wrap it in a function. It is not very useful or necessary here but illustrates how -tasks can be grouped. +Tool that downloads the configuration from the devices and +stores them on disk. """ -from brigade.core import Brigade -from brigade.core.configuration import Config -from brigade.plugins.inventory.simple import SimpleInventory +from brigade.easy import easy_brigade from brigade.plugins.tasks import files, networking, text import click def backup(task, path): + """ + This function groups two tasks: + 1. Download configuration from the device + 2. Store to disk + """ result = task.run(networking.napalm_get, + name="Gathering configuration from the device", getters="config") - return task.run(files.write, - content=result.result["config"]["running"], - filename="{}/{}".format(path, task.host)) + task.run(files.write, + name="Saving Configuration to disk", + content=result.result["config"]["running"], + filename="{}/{}".format(path, task.host)) @click.command() -@click.option('--filter', '-f', multiple=True) -@click.option('--path', '-p', default=".") +@click.option('--filter', '-f', multiple=True, + help="filters to apply. For instance site=cmh") +@click.option('--path', '-p', default=".", + help="Where to save the backup files") def main(filter, path): """ Backups running configuration of devices into a file """ - brigade = Brigade( - inventory=SimpleInventory("../hosts.yaml", "../groups.yaml"), - dry_run=False, - config=Config(raise_on_error=False), + brg = easy_brigade( + host_file="../inventory/hosts.yaml", + group_file="../inventory/groups.yaml", + dry_run=False, + raise_on_error=False, ) # filter is going to be a list of key=value so we clean that first @@ -43,13 +47,22 @@ def main(filter, path): k, v = f.split("=") filter_dict[k] = v - filtered = brigade.filter(**filter_dict) # let's filter the devices - results = filtered.run(backup, num_workers=20, path=path) + # let's filter the devices + filtered = brg.filter(**filter_dict) + + # Run the ``backup`` function that groups the tasks to + # download/store devices' configuration + results = filtered.run(backup, + name="Backing up configurations", + path=path) # Let's print the result on screen filtered.run(text.print_result, - num_workers=1, # we are printing on screen so we want to do this synchronously - data=results) + num_workers=1, # task should be done synchronously + data=results, + task_id=-1, # we only want to print the last task + skipped=True, + ) if __name__ == "__main__": diff --git a/examples/2_simple_tooling/configure.ipynb b/examples/2_simple_tooling/configure.ipynb new file mode 100644 index 00000000..fd28dd6c --- /dev/null +++ b/examples/2_simple_tooling/configure.ipynb @@ -0,0 +1,747 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# ignore this cell, this is just a helper cell to provide the magic %highlight_file\n", + "%run ../highlighter.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Configure\n", + "\n", + "## Code" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
 1 #!/usr/bin/env python\n",
+       " 2 """\n",
+       " 3 Tool to configure datacenter\n",
+       " 4 """\n",
+       " 5 from brigade.easy import easy_brigade\n",
+       " 6 from brigade.plugins.tasks import data, networking, text\n",
+       " 7 \n",
+       " 8 import click\n",
+       " 9 \n",
+       "10 \n",
+       "11 def configure(task):\n",
+       "12     """\n",
+       "13     This function groups all the tasks needed to configure the\n",
+       "14     network:\n",
+       "15 \n",
+       "16         1. Loading extra data\n",
+       "17         2. Templates to build configuration\n",
+       "18         3. Deploy configuration on the device\n",
+       "19     """\n",
+       "20     r = task.run(text.template_file,\n",
+       "21                  name="Base Configuration",\n",
+       "22                  template="base.j2",\n",
+       "23                  path="../templates/{brigade_nos}")\n",
+       "24     # r.result holds the result of rendering the template\n",
+       "25     # we store in the host itself so we can keep updating\n",
+       "26     # it as we render other templates\n",
+       "27     task.host["config"] = r.result\n",
+       "28 \n",
+       "29     r = task.run(data.load_yaml,\n",
+       "30                  name="Loading extra data",\n",
+       "31                  file="../extra_data/{host}/l3.yaml")\n",
+       "32     # r.result holds the data contained in the yaml files\n",
+       "33     # we load the data inside the host itself for further use\n",
+       "34     task.host["l3"] = r.result\n",
+       "35 \n",
+       "36     r = task.run(text.template_file,\n",
+       "37                  name="Interfaces Configuration",\n",
+       "38                  template="interfaces.j2",\n",
+       "39                  path="../templates/{brigade_nos}")\n",
+       "40     # we update our hosts' config\n",
+       "41     task.host["config"] += r.result\n",
+       "42 \n",
+       "43     r = task.run(text.template_file,\n",
+       "44                  name="Routing Configuration",\n",
+       "45                  template="routing.j2",\n",
+       "46                  path="../templates/{brigade_nos}")\n",
+       "47     # we update our hosts' config\n",
+       "48     task.host["config"] += r.result\n",
+       "49 \n",
+       "50     r = task.run(text.template_file,\n",
+       "51                  name="Role-specific Configuration",\n",
+       "52                  template="{role}.j2",\n",
+       "53                  path="../templates/{brigade_nos}")\n",
+       "54     # we update our hosts' config\n",
+       "55     task.host["config"] += r.result\n",
+       "56 \n",
+       "57     task.run(networking.napalm_configure,\n",
+       "58              name="Loading Configuration on the device",\n",
+       "59              replace=False,\n",
+       "60              configuration=task.host["config"])\n",
+       "61 \n",
+       "62 \n",
+       "63 @click.command()\n",
+       "64 @click.option('--filter', '-f', multiple=True,\n",
+       "65               help="k=v pairs to filter the devices")\n",
+       "66 @click.option('--commit/--no-commit', '-c', default=False,\n",
+       "67               help="whether you want to commit the changes or not")\n",
+       "68 def main(filter, commit):\n",
+       "69     brg = easy_brigade(\n",
+       "70             host_file="../inventory/hosts.yaml",\n",
+       "71             group_file="../inventory/groups.yaml",\n",
+       "72             dry_run=not commit,\n",
+       "73             raise_on_error=False,\n",
+       "74     )\n",
+       "75 \n",
+       "76     # filter is going to be a list of key=value so we clean that first\n",
+       "77     filter_dict = {"type": "network_device"}\n",
+       "78     for f in filter:\n",
+       "79         k, v = f.split("=")\n",
+       "80         filter_dict[k] = v\n",
+       "81 \n",
+       "82     # let's filter the devices\n",
+       "83     filtered = brg.filter(**filter_dict)\n",
+       "84 \n",
+       "85     results = filtered.run(task=configure)\n",
+       "86 \n",
+       "87     filtered.run(text.print_result,\n",
+       "88                  num_workers=1,  # task should be done synchronously\n",
+       "89                  data=results,\n",
+       "90                  task_id=-1,  # we only want to print the last task\n",
+       "91                  skipped=True,\n",
+       "92                  )\n",
+       "93 \n",
+       "94 \n",
+       "95 if __name__ == "__main__":\n",
+       "96     main()\n",
+       "
\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%highlight_file configure.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Demo\n", + "\n", + "Let's start with the help so we can see what we can do." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Usage: configure.py [OPTIONS]\n", + "\n", + "Options:\n", + " -f, --filter TEXT k=v pairs to filter the devices\n", + " -c, --commit / --no-commit whether you want to commit the changes or not\n", + " --help Show this message and exit.\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run configure.py --help" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With those options it should be easy to check which changes are to be applied before even applying them." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[33m* spine00.cmh ** changed : True ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m@@ -7,6 +7,9 @@\n", + " action bash sudo /mnt/flash/initialize_ma1.sh\n", + " !\n", + " transceiver qsfp default-mode 4x10G\n", + "+!\n", + "+hostname spine00.cmh\n", + "+ip domain-name cmh.acme.com\n", + " !\n", + " spanning-tree mode mstp\n", + " !\n", + "@@ -18,13 +21,28 @@\n", + " username vagrant privilege 15 role network-admin secret sha512 $6$Ga4ejrWPFsycSHFN$IJoLAEfCFHqiOwZX/PHlcx5vZ.Hpfx3NxHQXXEuf.Ni3QKlYL108fHruK86rzCjh9aYvBzoQ/ljLSy09.p6Z6/\n", + " !\n", + " interface Ethernet1\n", + "+ description link to leaf00.cmh\n", + "+ no switchport\n", + "+ ip address 10.0.0.0/31\n", + " !\n", + " interface Ethernet2\n", + "+ description link to leaf01.cmh\n", + "+ no switchport\n", + "+ ip address 10.0.0.2/31\n", + " !\n", + " interface Management1\n", + " ip address 10.0.2.15/24\n", + " !\n", + "-no ip routing\n", + "+ip routing\n", + "+!\n", + "+router bgp 65000\n", + "+ neighbor 10.0.0.1 remote-as 65100\n", + "+ neighbor 10.0.0.1 maximum-routes 12000 \n", + "+ neighbor 10.0.0.3 remote-as 65101\n", + "+ neighbor 10.0.0.3 maximum-routes 12000 \n", + "+ address-family ipv4\n", + "+ neighbor 10.0.0.1 activate\n", + "+ neighbor 10.0.0.3 activate\n", + " !\n", + " management api http-commands\n", + " no shutdown\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* spine01.cmh ** changed : True ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m[edit system]\n", + "- host-name vsrx;\n", + "+ host-name spine01.cmh;\n", + "+ domain-name cmh.acme.com;\n", + "[edit interfaces]\n", + "+ ge-0/0/1 {\n", + "+ description \"link to leaf00.cmh\";\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ address 10.0.1.0/31;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ ge-0/0/2 {\n", + "+ description \"link to leaf01.cmh\";\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ address 10.0.1.2/31;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "[edit]\n", + "+ routing-options {\n", + "+ autonomous-system 65000;\n", + "+ }\n", + "+ protocols {\n", + "+ bgp {\n", + "+ import PERMIT_ALL;\n", + "+ export PERMIT_ALL;\n", + "+ group peers {\n", + "+ neighbor 10.0.1.1 {\n", + "+ peer-as 65100;\n", + "+ }\n", + "+ neighbor 10.0.1.3 {\n", + "+ peer-as 65101;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ policy-options {\n", + "+ policy-statement PERMIT_ALL {\n", + "+ from protocol bgp;\n", + "+ then accept;\n", + "+ }\n", + "+ }\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* leaf00.cmh ** changed : True *************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m@@ -8,6 +8,9 @@\n", + " !\n", + " transceiver qsfp default-mode 4x10G\n", + " !\n", + "+hostname leaf00.cmh\n", + "+ip domain-name cmh.acme.com\n", + "+!\n", + " spanning-tree mode mstp\n", + " !\n", + " aaa authorization exec default local\n", + "@@ -17,14 +20,36 @@\n", + " username admin privilege 15 role network-admin secret sha512 $6$JA1pT7X2JMgpNLbD$mqA6evjEvg1wN09FOs9zniHg63Q.t7DEGEE5mxjXbmzLn5BI4H0OYjramSH5TTwsIyrBbTVbEv49dzeHqpYD4/\n", + " username vagrant privilege 15 role network-admin secret sha512 $6$MZz3VvL4.drK.FFg$lgXW.Fcb9rxxhAoYPg/GxFKAKVxrDEsPmeVNxxn8IH7RnRDRgZltqjPdpq53XYPaeGQO51MZ1qt30ziPwKbDl0\n", + " !\n", + "+vlan 100\n", + "+ name frontend\n", + "+!\n", + "+vlan 200\n", + "+ name backend\n", + "+!\n", + " interface Ethernet1\n", + "+ description link to spine00.cmh\n", + "+ shutdown\n", + "+ no switchport\n", + "+ ip address 10.0.0.1/31\n", + " !\n", + " interface Ethernet2\n", + "+ description link to spine01.cmh\n", + "+ no switchport\n", + "+ ip address 10.0.1.1/31\n", + " !\n", + " interface Management1\n", + " ip address 10.0.2.15/24\n", + " !\n", + "-no ip routing\n", + "+ip routing\n", + "+!\n", + "+router bgp 65100\n", + "+ neighbor 10.0.0.0 remote-as 65000\n", + "+ neighbor 10.0.0.0 maximum-routes 12000 \n", + "+ neighbor 10.0.1.0 remote-as 65000\n", + "+ neighbor 10.0.1.0 maximum-routes 12000 \n", + "+ address-family ipv4\n", + "+ neighbor 10.0.0.0 activate\n", + "+ neighbor 10.0.1.0 activate\n", + " !\n", + " management api http-commands\n", + " no shutdown\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* leaf01.cmh ** changed : True *************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m[edit system]\n", + "- host-name vsrx;\n", + "+ host-name leaf01.cmh;\n", + "+ domain-name cmh.acme.com;\n", + "[edit interfaces]\n", + "+ ge-0/0/1 {\n", + "+ description \"link to spine00.cmh\";\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ address 10.0.0.3/31;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ ge-0/0/2 {\n", + "+ description \"link to spine01.cmh\";\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ address 10.0.1.3/31;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "[edit]\n", + "+ routing-options {\n", + "+ autonomous-system 65101;\n", + "+ }\n", + "+ protocols {\n", + "+ bgp {\n", + "+ import PERMIT_ALL;\n", + "+ export PERMIT_ALL;\n", + "+ group peers {\n", + "+ neighbor 10.0.0.2 {\n", + "+ peer-as 65000;\n", + "+ }\n", + "+ neighbor 10.0.1.2 {\n", + "+ peer-as 65000;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ policy-options {\n", + "+ policy-statement PERMIT_ALL {\n", + "+ from protocol bgp;\n", + "+ then accept;\n", + "+ }\n", + "+ }\n", + "+ vlans {\n", + "+ backend {\n", + "+ vlan-id 200;\n", + "+ }\n", + "+ frontend {\n", + "+ vlan-id 100;\n", + "+ }\n", + "+ }\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run configure.py --filter site=cmh --no-commit" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can review the changes and commit them if we are happy:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[33m* spine00.cmh ** changed : True ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m@@ -7,6 +7,9 @@\n", + " action bash sudo /mnt/flash/initialize_ma1.sh\n", + " !\n", + " transceiver qsfp default-mode 4x10G\n", + "+!\n", + "+hostname spine00.cmh\n", + "+ip domain-name cmh.acme.com\n", + " !\n", + " spanning-tree mode mstp\n", + " !\n", + "@@ -18,13 +21,28 @@\n", + " username vagrant privilege 15 role network-admin secret sha512 $6$Ga4ejrWPFsycSHFN$IJoLAEfCFHqiOwZX/PHlcx5vZ.Hpfx3NxHQXXEuf.Ni3QKlYL108fHruK86rzCjh9aYvBzoQ/ljLSy09.p6Z6/\n", + " !\n", + " interface Ethernet1\n", + "+ description link to leaf00.cmh\n", + "+ no switchport\n", + "+ ip address 10.0.0.0/31\n", + " !\n", + " interface Ethernet2\n", + "+ description link to leaf01.cmh\n", + "+ no switchport\n", + "+ ip address 10.0.0.2/31\n", + " !\n", + " interface Management1\n", + " ip address 10.0.2.15/24\n", + " !\n", + "-no ip routing\n", + "+ip routing\n", + "+!\n", + "+router bgp 65000\n", + "+ neighbor 10.0.0.1 remote-as 65100\n", + "+ neighbor 10.0.0.1 maximum-routes 12000 \n", + "+ neighbor 10.0.0.3 remote-as 65101\n", + "+ neighbor 10.0.0.3 maximum-routes 12000 \n", + "+ address-family ipv4\n", + "+ neighbor 10.0.0.1 activate\n", + "+ neighbor 10.0.0.3 activate\n", + " !\n", + " management api http-commands\n", + " no shutdown\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* spine01.cmh ** changed : True ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m[edit system]\n", + "- host-name vsrx;\n", + "+ host-name spine01.cmh;\n", + "+ domain-name cmh.acme.com;\n", + "[edit interfaces]\n", + "+ ge-0/0/1 {\n", + "+ description \"link to leaf00.cmh\";\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ address 10.0.1.0/31;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ ge-0/0/2 {\n", + "+ description \"link to leaf01.cmh\";\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ address 10.0.1.2/31;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "[edit]\n", + "+ routing-options {\n", + "+ autonomous-system 65000;\n", + "+ }\n", + "+ protocols {\n", + "+ bgp {\n", + "+ import PERMIT_ALL;\n", + "+ export PERMIT_ALL;\n", + "+ group peers {\n", + "+ neighbor 10.0.1.1 {\n", + "+ peer-as 65100;\n", + "+ }\n", + "+ neighbor 10.0.1.3 {\n", + "+ peer-as 65101;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ policy-options {\n", + "+ policy-statement PERMIT_ALL {\n", + "+ from protocol bgp;\n", + "+ then accept;\n", + "+ }\n", + "+ }\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* leaf00.cmh ** changed : True *************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m@@ -8,6 +8,9 @@\n", + " !\n", + " transceiver qsfp default-mode 4x10G\n", + " !\n", + "+hostname leaf00.cmh\n", + "+ip domain-name cmh.acme.com\n", + "+!\n", + " spanning-tree mode mstp\n", + " !\n", + " aaa authorization exec default local\n", + "@@ -17,14 +20,36 @@\n", + " username admin privilege 15 role network-admin secret sha512 $6$JA1pT7X2JMgpNLbD$mqA6evjEvg1wN09FOs9zniHg63Q.t7DEGEE5mxjXbmzLn5BI4H0OYjramSH5TTwsIyrBbTVbEv49dzeHqpYD4/\n", + " username vagrant privilege 15 role network-admin secret sha512 $6$MZz3VvL4.drK.FFg$lgXW.Fcb9rxxhAoYPg/GxFKAKVxrDEsPmeVNxxn8IH7RnRDRgZltqjPdpq53XYPaeGQO51MZ1qt30ziPwKbDl0\n", + " !\n", + "+vlan 100\n", + "+ name frontend\n", + "+!\n", + "+vlan 200\n", + "+ name backend\n", + "+!\n", + " interface Ethernet1\n", + "+ description link to spine00.cmh\n", + "+ shutdown\n", + "+ no switchport\n", + "+ ip address 10.0.0.1/31\n", + " !\n", + " interface Ethernet2\n", + "+ description link to spine01.cmh\n", + "+ no switchport\n", + "+ ip address 10.0.1.1/31\n", + " !\n", + " interface Management1\n", + " ip address 10.0.2.15/24\n", + " !\n", + "-no ip routing\n", + "+ip routing\n", + "+!\n", + "+router bgp 65100\n", + "+ neighbor 10.0.0.0 remote-as 65000\n", + "+ neighbor 10.0.0.0 maximum-routes 12000 \n", + "+ neighbor 10.0.1.0 remote-as 65000\n", + "+ neighbor 10.0.1.0 maximum-routes 12000 \n", + "+ address-family ipv4\n", + "+ neighbor 10.0.0.0 activate\n", + "+ neighbor 10.0.1.0 activate\n", + " !\n", + " management api http-commands\n", + " no shutdown\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* leaf01.cmh ** changed : True *************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m[edit system]\n", + "- host-name vsrx;\n", + "+ host-name leaf01.cmh;\n", + "+ domain-name cmh.acme.com;\n", + "[edit interfaces]\n", + "+ ge-0/0/1 {\n", + "+ description \"link to spine00.cmh\";\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ address 10.0.0.3/31;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ ge-0/0/2 {\n", + "+ description \"link to spine01.cmh\";\n", + "+ unit 0 {\n", + "+ family inet {\n", + "+ address 10.0.1.3/31;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "[edit]\n", + "+ routing-options {\n", + "+ autonomous-system 65101;\n", + "+ }\n", + "+ protocols {\n", + "+ bgp {\n", + "+ import PERMIT_ALL;\n", + "+ export PERMIT_ALL;\n", + "+ group peers {\n", + "+ neighbor 10.0.0.2 {\n", + "+ peer-as 65000;\n", + "+ }\n", + "+ neighbor 10.0.1.2 {\n", + "+ peer-as 65000;\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ }\n", + "+ policy-options {\n", + "+ policy-statement PERMIT_ALL {\n", + "+ from protocol bgp;\n", + "+ then accept;\n", + "+ }\n", + "+ }\n", + "+ vlans {\n", + "+ backend {\n", + "+ vlan-id 200;\n", + "+ }\n", + "+ frontend {\n", + "+ vlan-id 100;\n", + "+ }\n", + "+ }\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run configure.py --filter site=cmh --commit" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If we run the tool again it should report no changes:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[34m* spine00.cmh ** changed : False ***********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : False -------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* spine01.cmh ** changed : False ***********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : False -------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* leaf00.cmh ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : False -------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* leaf01.cmh ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : False -------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run configure.py --filter site=cmh --no-commit" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/2_simple_tooling/configure.py b/examples/2_simple_tooling/configure.py index f0a16632..82b75744 100755 --- a/examples/2_simple_tooling/configure.py +++ b/examples/2_simple_tooling/configure.py @@ -1,53 +1,76 @@ #!/usr/bin/env python """ -In this example we write a CLI tool with brigade and click to deploy configuration. +Tool to configure datacenter """ -from brigade.core import Brigade -from brigade.core.configuration import Config -from brigade.plugins.inventory.simple import SimpleInventory +from brigade.easy import easy_brigade from brigade.plugins.tasks import data, networking, text import click def configure(task): + """ + This function groups all the tasks needed to configure the + network: + + 1. Loading extra data + 2. Templates to build configuration + 3. Deploy configuration on the device + """ r = task.run(text.template_file, + name="Base Configuration", template="base.j2", path="../templates/{brigade_nos}") + # r.result holds the result of rendering the template + # we store in the host itself so we can keep updating + # it as we render other templates task.host["config"] = r.result r = task.run(data.load_yaml, + name="Loading extra data", file="../extra_data/{host}/l3.yaml") + # r.result holds the data contained in the yaml files + # we load the data inside the host itself for further use task.host["l3"] = r.result r = task.run(text.template_file, + name="Interfaces Configuration", template="interfaces.j2", path="../templates/{brigade_nos}") + # we update our hosts' config task.host["config"] += r.result r = task.run(text.template_file, + name="Routing Configuration", template="routing.j2", path="../templates/{brigade_nos}") + # we update our hosts' config task.host["config"] += r.result r = task.run(text.template_file, + name="Role-specific Configuration", template="{role}.j2", path="../templates/{brigade_nos}") + # we update our hosts' config task.host["config"] += r.result - return task.run(networking.napalm_configure, - replace=False, - configuration=task.host["config"]) + task.run(networking.napalm_configure, + name="Loading Configuration on the device", + replace=False, + configuration=task.host["config"]) @click.command() -@click.option('--filter', '-f', multiple=True) -@click.option('--commit/--no-commit', '-c', default=False) +@click.option('--filter', '-f', multiple=True, + help="k=v pairs to filter the devices") +@click.option('--commit/--no-commit', '-c', default=False, + help="whether you want to commit the changes or not") def main(filter, commit): - brigade = Brigade( - inventory=SimpleInventory("../hosts.yaml", "../groups.yaml"), - dry_run=not commit, - config=Config(raise_on_error=False), + brg = easy_brigade( + host_file="../inventory/hosts.yaml", + group_file="../inventory/groups.yaml", + dry_run=not commit, + raise_on_error=False, ) # filter is going to be a list of key=value so we clean that first @@ -56,13 +79,17 @@ def main(filter, commit): k, v = f.split("=") filter_dict[k] = v - filtered = brigade.filter(**filter_dict) # let's filter the devices + # let's filter the devices + filtered = brg.filter(**filter_dict) results = filtered.run(task=configure) filtered.run(text.print_result, - num_workers=1, # we are printing on screen so we want to do this synchronously - data=results) + num_workers=1, # task should be done synchronously + data=results, + task_id=-1, # we only want to print the last task + skipped=True, + ) if __name__ == "__main__": diff --git a/examples/2_simple_tooling/get_facts.ipynb b/examples/2_simple_tooling/get_facts.ipynb new file mode 100644 index 00000000..87049422 --- /dev/null +++ b/examples/2_simple_tooling/get_facts.ipynb @@ -0,0 +1,306 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# ignore this cell, this is just a helper cell to provide the magic %highlight_file\n", + "%run ../highlighter.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Get facts\n", + "\n", + "## Code" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
 1 #!/usr/bin/env python\n",
+       " 2 """\n",
+       " 3 Very simple tool to get facts and print them on the screen.\n",
+       " 4 """\n",
+       " 5 from brigade.easy import easy_brigade\n",
+       " 6 from brigade.plugins.tasks import networking, text\n",
+       " 7 \n",
+       " 8 import click\n",
+       " 9 \n",
+       "10 \n",
+       "11 @click.command()\n",
+       "12 @click.option('--filter', '-f', multiple=True,\n",
+       "13               help="k=v pairs to filter the devices")\n",
+       "14 @click.option('--get', '-g', multiple=True,\n",
+       "15               help="getters you want to use")\n",
+       "16 def main(filter, get):\n",
+       "17     """\n",
+       "18     Retrieve information from network devices using napalm\n",
+       "19     """\n",
+       "20     brg = easy_brigade(\n",
+       "21             host_file="../inventory/hosts.yaml",\n",
+       "22             group_file="../inventory/groups.yaml",\n",
+       "23             dry_run=False,\n",
+       "24             raise_on_error=False,\n",
+       "25     )\n",
+       "26 \n",
+       "27     # filter is going to be a list of key=value so we clean that first\n",
+       "28     filter_dict = {"type": "network_device"}\n",
+       "29     for f in filter:\n",
+       "30         k, v = f.split("=")\n",
+       "31         filter_dict[k] = v\n",
+       "32 \n",
+       "33     # select which devices we want to work with\n",
+       "34     filtered = brg.filter(**filter_dict)\n",
+       "35     results = filtered.run(networking.napalm_get,\n",
+       "36                            getters=get)\n",
+       "37 \n",
+       "38     # Let's print the result on screen\n",
+       "39     filtered.run(text.print_result,\n",
+       "40                  num_workers=1,  # task should be done synchronously\n",
+       "41                  data=results)\n",
+       "42 \n",
+       "43 \n",
+       "44 if __name__ == "__main__":\n",
+       "45     main()\n",
+       "
\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%highlight_file get_facts.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Demo\n", + "\n", + "As usual, let's start with the help:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Usage: get_facts.py [OPTIONS]\n", + "\n", + " Retrieve information from network devices using napalm\n", + "\n", + "Options:\n", + " -f, --filter TEXT k=v pairs to filter the devices\n", + " -g, --get TEXT getters you want to use\n", + " --help Show this message and exit.\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run get_facts.py --help" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Looks like we can use any getter. Let's see:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[34m* spine00.cmh ** changed : False ***********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- napalm_get ** changed : False --------------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'interfaces'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'Ethernet1'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m'link to leaf00.cmh'\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m1516038050.9974556\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'08:00:27:0C:31:79'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m0\u001b[0m}\u001b[0m,\n", + " \u001b[0m'Ethernet2'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m'link to leaf01.cmh'\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m1516037549.5002303\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'08:00:27:0C:31:79'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m0\u001b[0m}\u001b[0m,\n", + " \u001b[0m'Management1'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'description'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'is_enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'is_up'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'last_flapped'\u001b[0m: \u001b[0m1516037563.4058475\u001b[0m,\n", + " \u001b[0m'mac_address'\u001b[0m: \u001b[0m'08:00:27:47:87:83'\u001b[0m,\n", + " \u001b[0m'speed'\u001b[0m: \u001b[0m1000\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run get_facts.py --filter name=spine00.cmh -g interfaces" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[34m* spine00.cmh ** changed : False ***********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- napalm_get ** changed : False --------------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'facts'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'fqdn'\u001b[0m: \u001b[0m'spine00.cmh.cmh.acme.com'\u001b[0m,\n", + " \u001b[0m'hostname'\u001b[0m: \u001b[0m'spine00.cmh'\u001b[0m,\n", + " \u001b[0m'interface_list'\u001b[0m: \u001b[0m['Ethernet1', 'Ethernet2', 'Management1']\u001b[0m,\n", + " \u001b[0m'model'\u001b[0m: \u001b[0m'vEOS'\u001b[0m,\n", + " \u001b[0m'os_version'\u001b[0m: \u001b[0m'4.17.5M-4414219.4175M'\u001b[0m,\n", + " \u001b[0m'serial_number'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'uptime'\u001b[0m: \u001b[0m1156\u001b[0m,\n", + " \u001b[0m'vendor'\u001b[0m: \u001b[0m'Arista'\u001b[0m}\u001b[0m,\n", + " \u001b[0m'users'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'admin'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'level'\u001b[0m: \u001b[0m15\u001b[0m,\n", + " \u001b[0m'password'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'role'\u001b[0m: \u001b[0m'network-admin'\u001b[0m,\n", + " \u001b[0m'sshkeys'\u001b[0m: \u001b[0m[]\u001b[0m}\u001b[0m,\n", + " \u001b[0m'vagrant'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'level'\u001b[0m: \u001b[0m15\u001b[0m,\n", + " \u001b[0m'password'\u001b[0m: \u001b[0m''\u001b[0m,\n", + " \u001b[0m'role'\u001b[0m: \u001b[0m'network-admin'\u001b[0m,\n", + " \u001b[0m'sshkeys'\u001b[0m: \u001b[0m[]\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run get_facts.py --filter name=spine00.cmh -g facts -g users" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/2_simple_tooling/get_facts.py b/examples/2_simple_tooling/get_facts.py index b8629905..8bbe60e0 100755 --- a/examples/2_simple_tooling/get_facts.py +++ b/examples/2_simple_tooling/get_facts.py @@ -1,15 +1,8 @@ #!/usr/bin/env python """ -This is a simple example where we use click and brigade to build a simple CLI tool to retrieve -hosts information. - -The main difference with get_facts_simple.py is that instead of calling a plugin directly -we wrap it in a function. It is not very useful or necessary here but illustrates how -tasks can be grouped. +Very simple tool to get facts and print them on the screen. """ -from brigade.core import Brigade -from brigade.core.configuration import Config -from brigade.plugins.inventory.simple import SimpleInventory +from brigade.easy import easy_brigade from brigade.plugins.tasks import networking, text import click @@ -24,10 +17,11 @@ def main(filter, get): """ Retrieve information from network devices using napalm """ - brigade = Brigade( - inventory=SimpleInventory("../hosts.yaml", "../groups.yaml"), - dry_run=True, - config=Config(raise_on_error=False), + brg = easy_brigade( + host_file="../inventory/hosts.yaml", + group_file="../inventory/groups.yaml", + dry_run=False, + raise_on_error=False, ) # filter is going to be a list of key=value so we clean that first @@ -36,13 +30,14 @@ def main(filter, get): k, v = f.split("=") filter_dict[k] = v - filtered = brigade.filter(**filter_dict) # let's filter the devices + # select which devices we want to work with + filtered = brg.filter(**filter_dict) results = filtered.run(networking.napalm_get, getters=get) # Let's print the result on screen filtered.run(text.print_result, - num_workers=1, # we are printing on screen so we want to do this synchronously + num_workers=1, # task should be done synchronously data=results) diff --git a/examples/2_simple_tooling/rollback.ipynb b/examples/2_simple_tooling/rollback.ipynb new file mode 100644 index 00000000..e09fc848 --- /dev/null +++ b/examples/2_simple_tooling/rollback.ipynb @@ -0,0 +1,411 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# ignore this cell, this is just a helper cell to provide the magic %highlight_file\n", + "%run ../highlighter.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Rollback\n", + "\n", + "## Code" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
 1 #!/usr/bin/env python\n",
+       " 2 """\n",
+       " 3 Tool to rollback configuration from a saved configuration\n",
+       " 4 """\n",
+       " 5 from brigade.easy import easy_brigade\n",
+       " 6 from brigade.plugins.tasks import networking, text\n",
+       " 7 \n",
+       " 8 import click\n",
+       " 9 \n",
+       "10 \n",
+       "11 def rollback(task, path):\n",
+       "12     """\n",
+       "13     This function loads the backup from ./$path/$hostname and\n",
+       "14     deploys it.\n",
+       "15     """\n",
+       "16     task.run(networking.napalm_configure,\n",
+       "17              name="Loading Configuration on the device",\n",
+       "18              replace=True,\n",
+       "19              filename="{}/{}".format(path, task.host))\n",
+       "20 \n",
+       "21 \n",
+       "22 @click.command()\n",
+       "23 @click.option('--filter', '-f', multiple=True,\n",
+       "24               help="k=v pairs to filter the devices")\n",
+       "25 @click.option('--commit/--no-commit', '-c', default=False,\n",
+       "26               help="whether you want to commit the changes or not")\n",
+       "27 @click.option('--path', '-p', default=".",\n",
+       "28               help="Where to save the backup files")\n",
+       "29 def main(filter, commit, path):\n",
+       "30     brg = easy_brigade(\n",
+       "31             host_file="../inventory/hosts.yaml",\n",
+       "32             group_file="../inventory/groups.yaml",\n",
+       "33             dry_run=not commit,\n",
+       "34             raise_on_error=True,\n",
+       "35     )\n",
+       "36 \n",
+       "37     # filter is going to be a list of key=value so we clean that first\n",
+       "38     filter_dict = {"type": "network_device"}\n",
+       "39     for f in filter:\n",
+       "40         k, v = f.split("=")\n",
+       "41         filter_dict[k] = v\n",
+       "42 \n",
+       "43     # let's filter the devices\n",
+       "44     filtered = brg.filter(**filter_dict)\n",
+       "45 \n",
+       "46     results = filtered.run(task=rollback, path=path)\n",
+       "47 \n",
+       "48     filtered.run(text.print_result,\n",
+       "49                  num_workers=1,  # task should be done synchronously\n",
+       "50                  data=results,\n",
+       "51                  task_id=-1,  # we only want to print the last task\n",
+       "52                  )\n",
+       "53 \n",
+       "54 \n",
+       "55 if __name__ == "__main__":\n",
+       "56     main()\n",
+       "
\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%highlight_file rollback.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Demo\n", + "\n", + "As usual, let's start with the help:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Usage: rollback.py [OPTIONS]\n", + "\n", + "Options:\n", + " -f, --filter TEXT k=v pairs to filter the devices\n", + " -c, --commit / --no-commit whether you want to commit the changes or not\n", + " -p, --path TEXT Where to save the backup files\n", + " --help Show this message and exit.\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run rollback.py --help" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Looks like we can filter devices as usual, we can test changes as with the ``configure.py`` tool and that we can even choose the path where to look for the configurations. Let's try it:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[33m* spine00.cmh ** changed : True ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m@@ -15,10 +15,10 @@\n", + " !\n", + " aaa authorization exec default local\n", + " !\n", + "-aaa root secret sha512 $6$DInx3sLBwR.ikKs8$ThNQ/uVPDT9YXcreGqYZ53IZA.abXsMPsV4jdylCVl5Nu6GMcVcFgvE9L3hvM/vZDJS7.1xs9ZwphFdP1BhNP1\n", + "+aaa root secret sha512 $6$5stn7z2imBLV6iO0$w0ZnOhy8SwNdELdO2da9q8wDKerYTyY8evY052UoyRJ2Wo6liaUneuTFGphL8JQD9gtESOipCBb6PYmSMuUjs.\n", + " !\n", + "-username admin privilege 15 role network-admin secret sha512 $6$JHsj9QpFAuo8TKNS$98Wmnr/.L2CCHtd6peL2q4fGOnt39C/XtBPJms6J/u1qBX9xWvf99FIYuQPSoqCTYBrN0ZNjzVKeIkNnV.Gez.\n", + "-username vagrant privilege 15 role network-admin secret sha512 $6$Ga4ejrWPFsycSHFN$IJoLAEfCFHqiOwZX/PHlcx5vZ.Hpfx3NxHQXXEuf.Ni3QKlYL108fHruK86rzCjh9aYvBzoQ/ljLSy09.p6Z6/\n", + "+username admin privilege 15 role network-admin secret sha512 $6$qkXlQpatVlanYe9v$aHTbPaGTaqDRCp5WSC3DPpDfblYSE24.OHeKgGOOTf0.Ol2lDpivTvHByx5tU41sVOGcHqc4U4LgrKv8AjbKQ/\n", + "+username vagrant privilege 15 role network-admin secret sha512 $6$kRQZJTqx69hOW5ag$Y6VX8Kk37TWEsriKdr6ixqvMuUSSbuFu2Eh/5SIet2TCeXP3bdlwikIAruPp6lHB5HdC.t6tPsZVctHMU7H590\n", + " !\n", + " interface Ethernet1\n", + " description link to leaf00.cmh\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* spine01.cmh ** changed : True ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m[edit system root-authentication]\n", + "+ ssh-rsa \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDi/i8iiAZsXC5qdmJZpTxKjUyyoMgEGoHXl/TMFdJjSV+XAZ18OXAEsvPO0AlXJ6RZTwK8Zcr6TLq4l1Kssd+kVN02shFkgDo3wWf3I2BXKKdog6/6fbhiD1SgCeafzWBlUQvREgDQDy1XSFjNjSJ39vtOa8ikqGdbf4XH0hjoLHYDV0H0VNZLboULCNFPF0PHQfPrsp2AXHU+p7sl61GhZgfw6WuLIzXWqJyq9B0Q5XgdmvnvdjZeTOShoPTPbaRYVVFOMGTqJQOZsl5P3wTIJT8JG7iEz1Tiar8nmltON83sy/lEODhZkJPXe3zw3fwUIS9yQ53z0t1UGHm7KGNX vagrant\"; ## SECRET-DATA\n", + "- ssh-rsa \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCcn03wv4SesN3JeAdwiOzgEdY5f+u4yptRK8OEjkHLuJg/6lR6MoD2BkdvWLShtx97/kVbxbTWOu9XM1mZ+E/YDR0mt7eHWwiy/OlgP9i0MzSj+XhtMUzRp7Ow+34VrrW7yQmuIkigq/QkDPv3b6O0u0y6azCQVrg5pvwRdZU2xTyKt/aM6/TL+glVh508XqG7RzlsmIRnrSa0WfHzcbQKPTJXlAjLGoYk53SltxW//e5HMQnTAJop0ic7FniXrVhS8F9iKxfLfFqzB5JJ2gaQX3y3cPr1MIg60aoSprI/8297wjE6fnQGcp1H1fD5rJx96m+3ViwydbtElhljcreB vagrant\"; ## SECRET-DATA\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* leaf00.cmh ** changed : True *************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m@@ -15,10 +15,10 @@\n", + " !\n", + " aaa authorization exec default local\n", + " !\n", + "-aaa root secret sha512 $6$8FLl5NfSda1Wh6d8$3SO4hP73eJnMf5kHrKUoMJ4jMtLU7iRrj/FJwAgAdk4GImfqy6WYLqMgmfpYOe6v/T4rjIFpOX..LmhCnbgbO0\n", + "+aaa root secret sha512 $6$sRifRAo/DXihW7sG$3r4MMTsslNCCWdD/FFIw3lvnnkI4SWO0bvhEzvWSurrOBgUsxjrmgN5kywH5Ta7LNNXiWjFfjwoyefn9nqeB2/\n", + " !\n", + "-username admin privilege 15 role network-admin secret sha512 $6$JA1pT7X2JMgpNLbD$mqA6evjEvg1wN09FOs9zniHg63Q.t7DEGEE5mxjXbmzLn5BI4H0OYjramSH5TTwsIyrBbTVbEv49dzeHqpYD4/\n", + "-username vagrant privilege 15 role network-admin secret sha512 $6$MZz3VvL4.drK.FFg$lgXW.Fcb9rxxhAoYPg/GxFKAKVxrDEsPmeVNxxn8IH7RnRDRgZltqjPdpq53XYPaeGQO51MZ1qt30ziPwKbDl0\n", + "+username admin privilege 15 role network-admin secret sha512 $6$/K1M3ENrC/xALAOm$1vCB5TfaI8ih5GQRCwhRE7KGzmc.EGuQZ7dEuwhP7AJC0/A97u88miINH/7GtrBpRZ.Inn5JY9tuymMcmyyKc.\n", + "+username vagrant privilege 15 role network-admin secret sha512 $6$9CGTCvCiiJK3lDMp$kU9ncPDBkw0w09.h9wIhQtMAkZ/1zD1ds/wlAZAtmSQf5ntNMjDgvmZpBcXWAPAETlk4.kA9niLTVmQwaLBV/.\n", + " !\n", + " vlan 100\n", + " name frontend\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* leaf01.cmh ** changed : True *************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m[edit system root-authentication]\n", + "+ ssh-rsa \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDsfGpEhGi8CbjIHJkMju/CJH6IuQiIzZyDt+AVieDfXKWDuBSOfc7YV8xNdYMqQqpDOWmEVZ7dhfD6IWDI3aa6WLkEXORD+zScjQo+5iHty6VlI61ImHQkWhWX6pZi3Cq/JsH8oldIC2xvzFNWB2p1suu+rzuGtJjbDq5NMlp1bNSiBgV0dHZR6Lt1UuK/rVBl7FbBN8HpInM+a37SkkwIrKMK8z42Ax9ufd17P3SqZP8oo+Ql4Y3aeCz2t4CfZNh9YRLZSiUYF16VN+31mzKEqT7+0rFlyfv/CaPwyfAv2BPFljUEsyFsWU923EGYQsfOIKVnd+zzHDHIHapVMQbh vagrant\"; ## SECRET-DATA\n", + "- ssh-rsa \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDuoXCl14JaKfWnyKSp1c4wjBv6XiCMsIRT7w0+BQxvaS7D1AoxNksYCTTjAJ8HVaMcLD7MI4bajS3/oEwtmCVpNJBG91UCi0P3tN2GjQwCwzrZG0eNpP2Gy51sKcq2lM1sxi+9QKYAtK5gmqV2Y8UeOuo4jKVNxCrPLYXO2BQBGCBUPayDjiPDir0H2BCKGpuwgegHgpkFKw+tWqo0IFsQmnvOQX+mjGDV8PVghCnzLO2ZbZrZPu5rRSgZm+CFGK1DGDsPBgdElxnu6ytjVIKkDzHrZ6HEm7yFgneb0WDGEmVl8MvBS9VPXXv8NzHJTUnedbxWKcqJ+xurpAGAYm6n vagrant\"; ## SECRET-DATA\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run rollback.py --path backups/cmh --no-commit --filter site=cmh" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Looks legit, let's commit the changes:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[33m* spine00.cmh ** changed : True ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m@@ -15,10 +15,10 @@\n", + " !\n", + " aaa authorization exec default local\n", + " !\n", + "-aaa root secret sha512 $6$DInx3sLBwR.ikKs8$ThNQ/uVPDT9YXcreGqYZ53IZA.abXsMPsV4jdylCVl5Nu6GMcVcFgvE9L3hvM/vZDJS7.1xs9ZwphFdP1BhNP1\n", + "+aaa root secret sha512 $6$5stn7z2imBLV6iO0$w0ZnOhy8SwNdELdO2da9q8wDKerYTyY8evY052UoyRJ2Wo6liaUneuTFGphL8JQD9gtESOipCBb6PYmSMuUjs.\n", + " !\n", + "-username admin privilege 15 role network-admin secret sha512 $6$JHsj9QpFAuo8TKNS$98Wmnr/.L2CCHtd6peL2q4fGOnt39C/XtBPJms6J/u1qBX9xWvf99FIYuQPSoqCTYBrN0ZNjzVKeIkNnV.Gez.\n", + "-username vagrant privilege 15 role network-admin secret sha512 $6$Ga4ejrWPFsycSHFN$IJoLAEfCFHqiOwZX/PHlcx5vZ.Hpfx3NxHQXXEuf.Ni3QKlYL108fHruK86rzCjh9aYvBzoQ/ljLSy09.p6Z6/\n", + "+username admin privilege 15 role network-admin secret sha512 $6$qkXlQpatVlanYe9v$aHTbPaGTaqDRCp5WSC3DPpDfblYSE24.OHeKgGOOTf0.Ol2lDpivTvHByx5tU41sVOGcHqc4U4LgrKv8AjbKQ/\n", + "+username vagrant privilege 15 role network-admin secret sha512 $6$kRQZJTqx69hOW5ag$Y6VX8Kk37TWEsriKdr6ixqvMuUSSbuFu2Eh/5SIet2TCeXP3bdlwikIAruPp6lHB5HdC.t6tPsZVctHMU7H590\n", + " !\n", + " interface Ethernet1\n", + " description link to leaf00.cmh\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* spine01.cmh ** changed : True ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m[edit system root-authentication]\n", + "+ ssh-rsa \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDi/i8iiAZsXC5qdmJZpTxKjUyyoMgEGoHXl/TMFdJjSV+XAZ18OXAEsvPO0AlXJ6RZTwK8Zcr6TLq4l1Kssd+kVN02shFkgDo3wWf3I2BXKKdog6/6fbhiD1SgCeafzWBlUQvREgDQDy1XSFjNjSJ39vtOa8ikqGdbf4XH0hjoLHYDV0H0VNZLboULCNFPF0PHQfPrsp2AXHU+p7sl61GhZgfw6WuLIzXWqJyq9B0Q5XgdmvnvdjZeTOShoPTPbaRYVVFOMGTqJQOZsl5P3wTIJT8JG7iEz1Tiar8nmltON83sy/lEODhZkJPXe3zw3fwUIS9yQ53z0t1UGHm7KGNX vagrant\"; ## SECRET-DATA\n", + "- ssh-rsa \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCcn03wv4SesN3JeAdwiOzgEdY5f+u4yptRK8OEjkHLuJg/6lR6MoD2BkdvWLShtx97/kVbxbTWOu9XM1mZ+E/YDR0mt7eHWwiy/OlgP9i0MzSj+XhtMUzRp7Ow+34VrrW7yQmuIkigq/QkDPv3b6O0u0y6azCQVrg5pvwRdZU2xTyKt/aM6/TL+glVh508XqG7RzlsmIRnrSa0WfHzcbQKPTJXlAjLGoYk53SltxW//e5HMQnTAJop0ic7FniXrVhS8F9iKxfLfFqzB5JJ2gaQX3y3cPr1MIg60aoSprI/8297wjE6fnQGcp1H1fD5rJx96m+3ViwydbtElhljcreB vagrant\"; ## SECRET-DATA\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* leaf00.cmh ** changed : True *************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m@@ -15,10 +15,10 @@\n", + " !\n", + " aaa authorization exec default local\n", + " !\n", + "-aaa root secret sha512 $6$8FLl5NfSda1Wh6d8$3SO4hP73eJnMf5kHrKUoMJ4jMtLU7iRrj/FJwAgAdk4GImfqy6WYLqMgmfpYOe6v/T4rjIFpOX..LmhCnbgbO0\n", + "+aaa root secret sha512 $6$sRifRAo/DXihW7sG$3r4MMTsslNCCWdD/FFIw3lvnnkI4SWO0bvhEzvWSurrOBgUsxjrmgN5kywH5Ta7LNNXiWjFfjwoyefn9nqeB2/\n", + " !\n", + "-username admin privilege 15 role network-admin secret sha512 $6$JA1pT7X2JMgpNLbD$mqA6evjEvg1wN09FOs9zniHg63Q.t7DEGEE5mxjXbmzLn5BI4H0OYjramSH5TTwsIyrBbTVbEv49dzeHqpYD4/\n", + "-username vagrant privilege 15 role network-admin secret sha512 $6$MZz3VvL4.drK.FFg$lgXW.Fcb9rxxhAoYPg/GxFKAKVxrDEsPmeVNxxn8IH7RnRDRgZltqjPdpq53XYPaeGQO51MZ1qt30ziPwKbDl0\n", + "+username admin privilege 15 role network-admin secret sha512 $6$/K1M3ENrC/xALAOm$1vCB5TfaI8ih5GQRCwhRE7KGzmc.EGuQZ7dEuwhP7AJC0/A97u88miINH/7GtrBpRZ.Inn5JY9tuymMcmyyKc.\n", + "+username vagrant privilege 15 role network-admin secret sha512 $6$9CGTCvCiiJK3lDMp$kU9ncPDBkw0w09.h9wIhQtMAkZ/1zD1ds/wlAZAtmSQf5ntNMjDgvmZpBcXWAPAETlk4.kA9niLTVmQwaLBV/.\n", + " !\n", + " vlan 100\n", + " name frontend\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[33m* leaf01.cmh ** changed : True *************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : True --------------------\u001b[0m\n", + "\u001b[0m[edit system root-authentication]\n", + "+ ssh-rsa \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDsfGpEhGi8CbjIHJkMju/CJH6IuQiIzZyDt+AVieDfXKWDuBSOfc7YV8xNdYMqQqpDOWmEVZ7dhfD6IWDI3aa6WLkEXORD+zScjQo+5iHty6VlI61ImHQkWhWX6pZi3Cq/JsH8oldIC2xvzFNWB2p1suu+rzuGtJjbDq5NMlp1bNSiBgV0dHZR6Lt1UuK/rVBl7FbBN8HpInM+a37SkkwIrKMK8z42Ax9ufd17P3SqZP8oo+Ql4Y3aeCz2t4CfZNh9YRLZSiUYF16VN+31mzKEqT7+0rFlyfv/CaPwyfAv2BPFljUEsyFsWU923EGYQsfOIKVnd+zzHDHIHapVMQbh vagrant\"; ## SECRET-DATA\n", + "- ssh-rsa \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDuoXCl14JaKfWnyKSp1c4wjBv6XiCMsIRT7w0+BQxvaS7D1AoxNksYCTTjAJ8HVaMcLD7MI4bajS3/oEwtmCVpNJBG91UCi0P3tN2GjQwCwzrZG0eNpP2Gy51sKcq2lM1sxi+9QKYAtK5gmqV2Y8UeOuo4jKVNxCrPLYXO2BQBGCBUPayDjiPDir0H2BCKGpuwgegHgpkFKw+tWqo0IFsQmnvOQX+mjGDV8PVghCnzLO2ZbZrZPu5rRSgZm+CFGK1DGDsPBgdElxnu6ytjVIKkDzHrZ6HEm7yFgneb0WDGEmVl8MvBS9VPXXv8NzHJTUnedbxWKcqJ+xurpAGAYm6n vagrant\"; ## SECRET-DATA\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run rollback.py --path backups/cmh --commit --filter site=cmh" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And let's verify all changes were applied correctly:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[34m* spine00.cmh ** changed : False ***********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : False -------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* spine01.cmh ** changed : False ***********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : False -------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* leaf00.cmh ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : False -------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* leaf01.cmh ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- Loading Configuration on the device ** changed : False -------------------\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run rollback.py --path backups/cmh --no-commit --filter site=cmh" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/2_simple_tooling/rollback.py b/examples/2_simple_tooling/rollback.py new file mode 100755 index 00000000..175d7dd5 --- /dev/null +++ b/examples/2_simple_tooling/rollback.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +""" +Tool to rollback configuration from a saved configuration +""" +from brigade.easy import easy_brigade +from brigade.plugins.tasks import networking, text + +import click + + +def rollback(task, path): + """ + This function loads the backup from ./$path/$hostname and + deploys it. + """ + task.run(networking.napalm_configure, + name="Loading Configuration on the device", + replace=True, + filename="{}/{}".format(path, task.host)) + + +@click.command() +@click.option('--filter', '-f', multiple=True, + help="k=v pairs to filter the devices") +@click.option('--commit/--no-commit', '-c', default=False, + help="whether you want to commit the changes or not") +@click.option('--path', '-p', default=".", + help="Where to save the backup files") +def main(filter, commit, path): + brg = easy_brigade( + host_file="../inventory/hosts.yaml", + group_file="../inventory/groups.yaml", + dry_run=not commit, + raise_on_error=True, + ) + + # filter is going to be a list of key=value so we clean that first + filter_dict = {"type": "network_device"} + for f in filter: + k, v = f.split("=") + filter_dict[k] = v + + # let's filter the devices + filtered = brg.filter(**filter_dict) + + results = filtered.run(task=rollback, path=path) + + filtered.run(text.print_result, + num_workers=1, # task should be done synchronously + data=results, + task_id=-1, # we only want to print the last task + ) + + +if __name__ == "__main__": + main() diff --git a/examples/2_simple_tooling/validate.ipynb b/examples/2_simple_tooling/validate.ipynb new file mode 100644 index 00000000..75711ffc --- /dev/null +++ b/examples/2_simple_tooling/validate.ipynb @@ -0,0 +1,404 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# ignore this cell, this is just a helper cell to provide the magic %highlight_file\n", + "%run ../highlighter.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Validate\n", + "\n", + "## Code" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
 1 #!/usr/bin/env python\n",
+       " 2 """\n",
+       " 3 Runbook that verifies that BGP sessions are configured and up.\n",
+       " 4 """\n",
+       " 5 from brigade.easy import easy_brigade\n",
+       " 6 from brigade.plugins.tasks import data, networking, text\n",
+       " 7 \n",
+       " 8 import click\n",
+       " 9 \n",
+       "10 \n",
+       "11 def validate(task):\n",
+       "12     task.host["config"] = ""\n",
+       "13 \n",
+       "14     r = task.run(name="read data",\n",
+       "15                  task=data.load_yaml,\n",
+       "16                  file="../extra_data/{host}/l3.yaml")\n",
+       "17 \n",
+       "18     validation_rules = [{\n",
+       "19         'get_bgp_neighbors': {\n",
+       "20             'global': {\n",
+       "21                 'peers': {\n",
+       "22                     '_mode': 'strict',\n",
+       "23                 }\n",
+       "24             }\n",
+       "25         }\n",
+       "26     }]\n",
+       "27     peers = validation_rules[0]['get_bgp_neighbors']['global']['peers']\n",
+       "28     for session in r.result['sessions']:\n",
+       "29         peers[session['ipv4']] = {'is_up': True}\n",
+       "30 \n",
+       "31     task.run(name="validating data",\n",
+       "32              task=networking.napalm_validate,\n",
+       "33              validation_source=validation_rules)\n",
+       "34 \n",
+       "35 \n",
+       "36 def print_compliance(task, results):\n",
+       "37     """\n",
+       "38     We use this task so we can access directly the result\n",
+       "39     for each specific host and see if the task complies or not\n",
+       "40     and pass it to print_result.\n",
+       "41     """\n",
+       "42     task.run(name="print result",\n",
+       "43              task=text.print_result,\n",
+       "44              data=results[task.host.name],\n",
+       "45              failed=not results[task.host.name][2].result['complies'],\n",
+       "46              )\n",
+       "47 \n",
+       "48 \n",
+       "49 @click.command()\n",
+       "50 @click.option('--filter', '-f', multiple=True,\n",
+       "51               help="k=v pairs to filter the devices")\n",
+       "52 def main(filter):\n",
+       "53     brg = easy_brigade(\n",
+       "54             host_file="../inventory/hosts.yaml",\n",
+       "55             group_file="../inventory/groups.yaml",\n",
+       "56             dry_run=False,\n",
+       "57             raise_on_error=True,\n",
+       "58     )\n",
+       "59 \n",
+       "60     # filter is going to be a list of key=value so we clean that first\n",
+       "61     filter_dict = {"type": "network_device"}\n",
+       "62     for f in filter:\n",
+       "63         k, v = f.split("=")\n",
+       "64         filter_dict[k] = v\n",
+       "65 \n",
+       "66     # select which devices we want to work with\n",
+       "67     filtered = brg.filter(**filter_dict)\n",
+       "68 \n",
+       "69     results = filtered.run(task=validate)\n",
+       "70 \n",
+       "71     filtered.run(print_compliance,\n",
+       "72                  results=results,\n",
+       "73                  num_workers=1)\n",
+       "74 \n",
+       "75 \n",
+       "76 if __name__ == "__main__":\n",
+       "77     main()\n",
+       "
\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%highlight_file validate.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Demo\n", + "\n", + "As usual, let's start with the help:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Usage: validate.py [OPTIONS]\n", + "\n", + "Options:\n", + " -f, --filter TEXT k=v pairs to filter the devices\n", + " --help Show this message and exit.\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run validate.py --help" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Not much to it, very similar to its runbook counterpart:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m\u001b[31m* spine00.cmh ** changed : False ***********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validate ** changed : False ----------------------------------------------\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- read data ** changed : False ---------------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'interfaces'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'Ethernet1'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'leaf00.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.0.0/31'\u001b[0m}\u001b[0m,\n", + " \u001b[0m'Ethernet2'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'leaf01.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.0.2/31'\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'sessions'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m\u001b[0m{'ipv4': '10.0.0.1', 'peer_as': 65100}\u001b[0m,\n", + " \u001b[0m{'ipv4': '10.0.0.3', 'peer_as': 65101}\u001b[0m]\u001b[0m}\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validating data ** changed : False ---------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'get_bgp_neighbors'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'missing'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'global'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'diff'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'missing'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'peers'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'diff'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m]\u001b[0m,\n", + " \u001b[0m'missing'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m]\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'10.0.0.1'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'diff'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m]\u001b[0m,\n", + " \u001b[0m'missing'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m]\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'is_up'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'actual_value'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'expected_value'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mFalse\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mTrue\u001b[0m}\u001b[0m,\n", + " \u001b[0m'10.0.0.3'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mTrue\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mTrue\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mTrue\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'skipped'\u001b[0m: \u001b[0m[]\u001b[0m}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* spine01.cmh ** changed : False ***********************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validate ** changed : False ----------------------------------------------\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- read data ** changed : False ---------------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'interfaces'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'ge-0/0/1'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'leaf00.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.1.0/31'\u001b[0m}\u001b[0m,\n", + " \u001b[0m'ge-0/0/2'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'leaf01.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.1.2/31'\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'sessions'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m\u001b[0m{'ipv4': '10.0.1.1', 'peer_as': 65100}\u001b[0m,\n", + " \u001b[0m{'ipv4': '10.0.1.3', 'peer_as': 65101}\u001b[0m]\u001b[0m}\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validating data ** changed : False ---------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'get_bgp_neighbors'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'missing'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'global'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mTrue\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'skipped'\u001b[0m: \u001b[0m[]\u001b[0m}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[31m* leaf00.cmh ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validate ** changed : False ----------------------------------------------\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- read data ** changed : False ---------------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'interfaces'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'Ethernet1'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'spine00.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.0.1/31'\u001b[0m}\u001b[0m,\n", + " \u001b[0m'Ethernet2'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'spine01.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.1.1/31'\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'sessions'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m\u001b[0m{'ipv4': '10.0.0.0', 'peer_as': 65000}\u001b[0m,\n", + " \u001b[0m{'ipv4': '10.0.1.0', 'peer_as': 65000}\u001b[0m]\u001b[0m}\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validating data ** changed : False ---------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'get_bgp_neighbors'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'missing'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'global'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'diff'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'missing'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'peers'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'diff'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m]\u001b[0m,\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \u001b[0m'missing'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m]\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'10.0.0.0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'diff'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m]\u001b[0m,\n", + " \u001b[0m'missing'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m]\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'is_up'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'actual_value'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'complies'\u001b[0m: \u001b[0mFalse\u001b[0m,\n", + " \u001b[0m'expected_value'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mFalse\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mTrue\u001b[0m}\u001b[0m,\n", + " \u001b[0m'10.0.1.0'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mTrue\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mTrue\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mTrue\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'skipped'\u001b[0m: \u001b[0m[]\u001b[0m}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[34m* leaf01.cmh ** changed : False ************************************************\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validate ** changed : False ----------------------------------------------\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- read data ** changed : False ---------------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'interfaces'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'ge-0/0/1'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'spine00.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.0.3/31'\u001b[0m}\u001b[0m,\n", + " \u001b[0m'ge-0/0/2'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'connects_to'\u001b[0m: \u001b[0m'spine01.cmh'\u001b[0m,\n", + " \u001b[0m'enabled'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'ipv4'\u001b[0m: \u001b[0m'10.0.1.3/31'\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'sessions'\u001b[0m: \u001b[0m[\u001b[0m \u001b[0m\u001b[0m{'ipv4': '10.0.0.2', 'peer_as': 65000}\u001b[0m,\n", + " \u001b[0m{'ipv4': '10.0.1.2', 'peer_as': 65000}\u001b[0m]\u001b[0m}\u001b[0m\n", + "\u001b[0m\u001b[1m\u001b[36m---- validating data ** changed : False ---------------------------------------\u001b[0m\n", + "\u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'get_bgp_neighbors'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'extra'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'missing'\u001b[0m: \u001b[0m[]\u001b[0m,\n", + " \u001b[0m'present'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'global'\u001b[0m: \u001b[0m{\u001b[0m \u001b[0m'complies'\u001b[0m: \u001b[0mTrue\u001b[0m,\n", + " \u001b[0m'nested'\u001b[0m: \u001b[0mTrue\u001b[0m}\u001b[0m}\u001b[0m}\u001b[0m,\n", + " \u001b[0m'skipped'\u001b[0m: \u001b[0m[]\u001b[0m}\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m" + ] + } + ], + "source": [ + "%run validate.py --filter site=cmh" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/2_simple_tooling/validate.py b/examples/2_simple_tooling/validate.py index a64a928f..04597074 100755 --- a/examples/2_simple_tooling/validate.py +++ b/examples/2_simple_tooling/validate.py @@ -1,9 +1,8 @@ #!/usr/bin/env python """ -In this example we write a CLI tool with brigade and click to deploy configuration. +Runbook that verifies that BGP sessions are configured and up. """ -from brigade.core import Brigade -from brigade.plugins.inventory.simple import SimpleInventory +from brigade.easy import easy_brigade from brigade.plugins.tasks import data, networking, text import click @@ -29,26 +28,33 @@ def validate(task): for session in r.result['sessions']: peers[session['ipv4']] = {'is_up': True} - return task.run(name="validating data", - task=networking.napalm_validate, - validation_source=validation_rules) + task.run(name="validating data", + task=networking.napalm_validate, + validation_source=validation_rules) def print_compliance(task, results): + """ + We use this task so we can access directly the result + for each specific host and see if the task complies or not + and pass it to print_result. + """ task.run(name="print result", task=text.print_result, data=results[task.host.name], - failed=not results[task.host.name].result['complies'], + failed=not results[task.host.name][2].result['complies'], ) @click.command() -@click.option('--filter', '-f', multiple=True) -@click.option('--commit/--no-commit', '-c', default=False) -def main(filter, commit): - brigade = Brigade( - inventory=SimpleInventory("../hosts.yaml", "../groups.yaml"), - dry_run=False, +@click.option('--filter', '-f', multiple=True, + help="k=v pairs to filter the devices") +def main(filter): + brg = easy_brigade( + host_file="../inventory/hosts.yaml", + group_file="../inventory/groups.yaml", + dry_run=False, + raise_on_error=True, ) # filter is going to be a list of key=value so we clean that first @@ -57,7 +63,8 @@ def main(filter, commit): k, v = f.split("=") filter_dict[k] = v - filtered = brigade.filter(**filter_dict) # let's filter the devices + # select which devices we want to work with + filtered = brg.filter(**filter_dict) results = filtered.run(task=validate) diff --git a/examples/highlighter.py b/examples/highlighter.py new file mode 100644 index 00000000..1e1c53fe --- /dev/null +++ b/examples/highlighter.py @@ -0,0 +1,34 @@ +from __future__ import print_function + +from IPython.core.magic import register_line_magic +from IPython.display import HTML + +from pygments import highlight +from pygments.formatters import HtmlFormatter +from pygments.lexers import get_lexer_for_filename, get_lexer_by_name + + +HTML_TEMPLATE = """ +{} +""" + + +@register_line_magic +def highlight_file(filename): + lexer = get_lexer_by_name("py3") + + linenos = "inline" + + formatter = HtmlFormatter(style='default', + cssclass='pygments', + linenos=linenos) + + with open(filename) as f: + code = f.read() + + html_code = highlight(code, lexer, formatter) + css = formatter.get_style_defs() + + return HTML(HTML_TEMPLATE.format(css, html_code)) From 7e1bb533df1d0183ae180eaec7284097ed494bc6 Mon Sep 17 00:00:00 2001 From: David Barroso Date: Sat, 20 Jan 2018 17:44:48 +0100 Subject: [PATCH 65/67] minor fixes proposed in the comments --- brigade/core/task.py | 2 +- brigade/easy.py | 2 +- brigade/plugins/tasks/files/write.py | 8 ++--- docs/tutorials/intro/brigade.rst | 4 +-- docs/tutorials/intro/inventory.rst | 34 ++++++++++-------- docs/tutorials/intro/running_tasks_errors.rst | 8 ++--- examples/1_simple_runbooks/rollback.py | 3 +- examples/highlighter.py | 2 +- examples/inventory/groups.yaml | 1 - tests/inventory_data/nsot/nsot.sqlite3 | Bin 245760 -> 251904 bytes 10 files changed, 34 insertions(+), 30 deletions(-) diff --git a/brigade/core/task.py b/brigade/core/task.py index a1af8ad4..baa49813 100644 --- a/brigade/core/task.py +++ b/brigade/core/task.py @@ -20,7 +20,7 @@ class Task(object): name (``string``): name of task, defaults to ``task.__name__`` skipped (``bool``): whether to run hosts that should be skipped otherwise or not params: Parameters that will be passed to the ``task``. - self.results (:obj:`brigade.core.tasks.MultiResult`): Intermediate results + self.results (:obj:`brigade.core.task.MultiResult`): Intermediate results host (:obj:`brigade.core.inventory.Host`): Host we are operating with. Populated right before calling the ``task`` brigade(:obj:`brigade.core.Brigade`): Populated right before calling diff --git a/brigade/easy.py b/brigade/easy.py index f05ba6aa..8726e9a5 100644 --- a/brigade/easy.py +++ b/brigade/easy.py @@ -3,7 +3,7 @@ from brigade.plugins.inventory.simple import SimpleInventory -def easy_brigade(host_file="host.yaml", group_file="groups.yaml", dry_run=True, **kwargs): +def easy_brigade(host_file="host.yaml", group_file="groups.yaml", dry_run=False, **kwargs): """ Helper function to create easily a :obj:`brigade.core.Brigade` object. diff --git a/brigade/plugins/tasks/files/write.py b/brigade/plugins/tasks/files/write.py index 6009e28a..4bc28d4e 100644 --- a/brigade/plugins/tasks/files/write.py +++ b/brigade/plugins/tasks/files/write.py @@ -4,15 +4,15 @@ from brigade.core.task import Result -def read_file(file): +def _read_file(file): if not os.path.exists(file): return [] with open(file, "r") as f: return f.read().splitlines() -def generate_diff(filename, content, append): - original = read_file(filename) +def _generate_diff(filename, content, append): + original = _read_file(filename) if append: c = list(original) c.extend(content.splitlines()) @@ -38,7 +38,7 @@ def write(task, filename, content, append=False): * changed (``bool``): * diff (``str``): unified diff """ - diff = generate_diff(filename, content, append) + diff = _generate_diff(filename, content, append) if not task.dry_run: mode = "a+" if append else "w+" diff --git a/docs/tutorials/intro/brigade.rst b/docs/tutorials/intro/brigade.rst index ae5f7c10..2a5fb807 100644 --- a/docs/tutorials/intro/brigade.rst +++ b/docs/tutorials/intro/brigade.rst @@ -11,8 +11,8 @@ Using the "raw" API If you want to use the "raw" API you need two things: -1. A configuration object. -2. An inventory object. +1. A :obj:`brigade.core.configuration.Config` object. +2. An :doc:`inventory ` object. Once you have them, you can create the brigade object yourself. For example:: diff --git a/docs/tutorials/intro/inventory.rst b/docs/tutorials/intro/inventory.rst index 5cd1b481..a8f089cb 100644 --- a/docs/tutorials/intro/inventory.rst +++ b/docs/tutorials/intro/inventory.rst @@ -3,11 +3,28 @@ The Inventory The inventory is arguably the most important piece of Brigade. The inventory organizes hosts and makes sure tasks have the correct data for each host. + +Inventory data +-------------- + +Before we start let's take a look at the inventory data: + +* ``hosts.yaml`` + +.. literalinclude:: ../../../examples/inventory/hosts.yaml + +* ``groups.yaml`` + +.. literalinclude:: ../../../examples/inventory/groups.yaml + +Loading the inventory +--------------------- + You can create the inventory in different ways, depending on your data source. To see the available plugins you can use go to the :ref:`ref-inventory` reference guide. -.. note:: For this and the subsequent sections of this tutorial we are going to use the :obj:`SimpleInventory ` with the data located in ``/examples/inventory/``. We will also use the ``Vagrantfile`` located there so you should be able to reproduce everything. You can head to `Hosts/Groups contents`_ to see the contents of the file just for reference. +.. note:: For this and the subsequent sections of this tutorial we are going to use the :obj:`SimpleInventory ` with the data located in ``/examples/inventory/``. We will also use the ``Vagrantfile`` located there so you should be able to reproduce everything. -First, let's create the inventory:: +First, let's load the inventory:: >>> from brigade.plugins.inventory.simple import SimpleInventory >>> inventory = SimpleInventory(host_file="hosts.yaml", group_file="groups.yaml") @@ -116,16 +133,3 @@ You can also do more complex filtering by using functions or lambdas:: dict_keys(['host1.cmh', 'host2.cmh', 'host1.bma', 'host2.bma']) Not the most useful example but it should be enough to illustrate how it works. - - -Hosts/Groups contents ---------------------- - - -* ``hosts.yaml`` - -.. literalinclude:: ../../../examples/inventory/hosts.yaml - -* ``groups.yaml`` - -.. literalinclude:: ../../../examples/inventory/groups.yaml diff --git a/docs/tutorials/intro/running_tasks_errors.rst b/docs/tutorials/intro/running_tasks_errors.rst index 1ef3d1ac..7e34e1cc 100644 --- a/docs/tutorials/intro/running_tasks_errors.rst +++ b/docs/tutorials/intro/running_tasks_errors.rst @@ -1,10 +1,10 @@ Dealing with task errors ======================== -Tasks can fail due to many reasons. A continuation we will see how to deal with errors effectively with brigade. +Tasks can fail due to many reasons. As we continue we will see how to deal with errors effectively with brigade. -Failing by default ------------------- +Failing on error by default +--------------------------- Brigade can raise a :obj:`brigade.core.exceptions.BrigadeExecutionError` exception automatically as soon as an error occurs. For instance:: @@ -367,7 +367,7 @@ Regardless of the default behavior you can force ``raise_on_error`` on a per tas ... raise_on_error=False) >>> -As you can see, regardless of what ``brigade`` had configured to do, the task failed on the first case but didn't on the second one. +As you can see, regardless of what ``brigade`` had been configured to do, the task failed on the first case but didn't on the second one. Which one to use ---------------- diff --git a/examples/1_simple_runbooks/rollback.py b/examples/1_simple_runbooks/rollback.py index 655f07ac..2c219cc7 100755 --- a/examples/1_simple_runbooks/rollback.py +++ b/examples/1_simple_runbooks/rollback.py @@ -32,7 +32,6 @@ def main(filter, get): raise_on_error=True, ) - # select which devices we want to work with filtered = brg.filter(type="network_device", site="cmh") @@ -43,5 +42,7 @@ def main(filter, get): data=results, task_id=-1, # we only want to print the last task ) + + if __name__ == "__main__": main() diff --git a/examples/highlighter.py b/examples/highlighter.py index 1e1c53fe..ebe31b33 100644 --- a/examples/highlighter.py +++ b/examples/highlighter.py @@ -5,7 +5,7 @@ from pygments import highlight from pygments.formatters import HtmlFormatter -from pygments.lexers import get_lexer_for_filename, get_lexer_by_name +from pygments.lexers import get_lexer_by_name HTML_TEMPLATE = """