-
Notifications
You must be signed in to change notification settings - Fork 229
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
First stable release almost ready #68
Changes from 1 commit
92fed6f
37e1fa5
29f4c56
2a926df
b0ca3e0
8063186
eadbf11
a230151
3184632
027a440
5e59282
52f63f9
4972edd
cecc260
30e8570
0b265ce
3e89f8c
0e4027d
ca49110
1e06a18
4041eec
f830286
8b8a4d6
a7096c6
499d11d
308fd67
57853e2
7824871
b9a0d09
df26d81
707c9cc
7e1bb53
7b9eef3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,10 @@ | ||
Data | ||
#### | ||
|
||
.. autoclass:: brigade.core.Data | ||
:members: | ||
:undoc-members: | ||
|
||
Brigade | ||
####### | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
Easy | ||
==== | ||
|
||
.. automodule:: brigade.easy | ||
:members: | ||
:undoc-members: |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,5 +8,6 @@ Brigade API Reference | |
brigade | ||
configuration | ||
inventory | ||
easy | ||
task | ||
exceptions | ||
exceptions |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
Functions | ||
========= | ||
|
||
.. toctree:: | ||
:maxdepth: 2 | ||
:glob: | ||
|
||
* |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
Text | ||
==== | ||
|
||
.. automodule:: brigade.plugins.functions.text | ||
:members: | ||
:undoc-members: |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,4 +24,5 @@ Reference Guides | |
:caption: Plugins | ||
|
||
tasks/index | ||
functions/index | ||
inventory/index |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
.. _ref-inventory: | ||
|
||
Inventory | ||
========= | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Place a link to the Config 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.plugins.inventory.simple.SimpleInventory object at 0x10606bf28> | ||
>>> 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 <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. |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <brigade.plugins.inventory.simple.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:: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this say "create", not load? |
||
|
||
>>> 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} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One thing I don't like about this is that the output requires side scrolling to see it all. However I don't know if it would be better to print it vertically either since that will just make the page longer.. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I pretty strongly disliked the horizontal scrolling (it was close to unreadable). It would be much better if it displayed vertically. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, will try to fix. I am going to move the tutorial to a jupyter notebook anyway so we can run it and make sure it's still valid. I already moved most of the examples. |
||
>>> 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 "<stdin>", line 1, in <module> | ||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be helpful if this was displayed before the inventory was loaded. I think it makes it easier to understand what data we are looking at. |
||
--------------------- | ||
|
||
|
||
* ``hosts.yaml`` | ||
|
||
.. literalinclude:: ../../../examples/inventory/hosts.yaml | ||
|
||
* ``groups.yaml`` | ||
|
||
.. literalinclude:: ../../../examples/inventory/groups.yaml |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am going to add general comments about the tutorial here:
The part that is a bit strange is that we have names that are similar, but not identical. Should we just make them identical. Also should we have a one-to-one correspondence between attribute access and dictionary keys (i.e. all dictionary keys could just be accessed as
host.foo
)?brigade
as a variable name here:This doesn't work:
Instead needed to use:
Here is what I ended up doing to print the result:
I thought it was a bit strange in the output printing above that the result.items() that the
v
was a MultiResult and I needed to loop over it. The part that was strange to me was when we do theget_task(task)
later in this tutorial...in that situation we just get a result that we can print and not a MultiResult (or vice versa). In other words, it isn't clear to me why we return a MultiResult in one context and not in the other.There were some changes that I needed to make to get the
get_info(task)
to run properly. Once again for the import ofnapalm_get
and for the result printing:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay...maybe this is all old as it is in
docs/howto
and notdocs/tutorials
. Probably should only have one place and not three (examples
,docs/howto
,docs/tutorials
and I am 0 for 2 on reviewing the right one).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is due to the structure of the documentation in general. Saw that you were talking about this on Twitter too. Basically howto's aim to solve a specific task and tutorials is more handholding. Currently they might basically cover the same topics. Doesn't look like the howtos are included in this PR, but they probably need to be updated.
Also think that the examples are good for people who only want to view the code and not have it spreadout on a page with different sections as can happen in a tutorial.