Skip to content
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

Inventory rework #243

Merged
merged 44 commits into from
Oct 23, 2018
Merged

Inventory rework #243

merged 44 commits into from
Oct 23, 2018

Conversation

dbarrosop
Copy link
Contributor

@dbarrosop dbarrosop commented Sep 4, 2018

Needs to cleanup some bits but wanted to start with the review already :D

This PR builds on another one that needs even more cleaning but basically this PR does the following:

  1. Separates the serializer objects from the "core" objects. So now we have a nornir.core.deserializer.inventory file with classes based on pydantic that are used to serialize/deserialize the inventory. Those objects then map into regular classes that contains all the nornir logic.
  2. It creates proper structure to the inventory file having a proper schema. This means two things; there is clear separation between constructs and the schema can validate the entered data is correct, failing if a key is out of place, missing or misspelled.
  3. InitNornir has been moved to nornir.init_nornir (can be imported as from nornir import InitNornir. The motivation behind the move is that as mypy starts to get enabled cyclic imports start to show up clearly suggesting code smell. For instance, in the core we were importing the connection plugins in order to register them, but then the plugins also needed to import the core as the connection superclass lives there. The solution? Well, InitNornir is meant to be initialization code that ties all the different parts together so it's clearly not part of the "core" itself. Moving it outside the core solves the cyclic imports.

You can see the schema by doing:

import json
from nornir.core.deserializer.inventory import Inventory

print(json.dumps(Inventory.schema(), indent=4))

I updated the relevant tutorials:

Also, note that the well-known attributes are now attributes as opposed to items (host.username, host.password...) while items are user-defined and free (host["my_custom_var_nornir_doesnt_care"])

@coveralls
Copy link

coveralls commented Sep 4, 2018

Pull Request Test Coverage Report for Build 987

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage decreased (-1.1%) to 92.297%

Totals Coverage Status
Change from base Build 985: -1.1%
Covered Lines: 1390
Relevant Lines: 1506

💛 - Coveralls

@ktbyers
Copy link
Collaborator

ktbyers commented Sep 4, 2018

Note, I removed some earlier comments as I had errors in them.

@dbarrosop dbarrosop changed the title [WIP] Inventory rework Inventory rework Sep 9, 2018
@dbarrosop
Copy link
Contributor Author

This one is ready for review.

@ktbyers
Copy link
Collaborator

ktbyers commented Sep 9, 2018

Okay, starting to look at this. I looked at earlier version quite a bit early last week.

nornir/__init__.py Show resolved Hide resolved
v = object.__getattribute__(self, name)
if v is None:
for g in self.groups.refs:
r = object.__getattribute__(self, name)
Copy link
Collaborator

@ktbyers ktbyers Sep 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This behavior looks wrong here?

In my inventory I have a port defined as follows in groups.yaml:

nxos:
  port: 8443
  connection_options:
    napalm:
      platform: nxos
      extras:
        timeout: 300 
        optional_args:
          port: 8443
  data:
    foo: whatever

But 'port' won't resolve if I try to access it via a host that belongs to the nxos group. Digging into this a bunch more with Pdb.

The current variables are as follows:

(Pdb) p v
None
(Pdb) p g
Group: nxos
(Pdb) p self.groups.refs
[Group: nxos]
(Pdb) p self
Host: nxos1
(Pdb) 

Shouldn't that line 181 be:

r = object.__getattribute__(g, name)

The code for r = object.__getattribute__(self, name) is the same as the code for v = ... right above it (which we already know is None in this context).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mmmm, so I may have changed the behavior and now you either have to override all the attributes or none. I don't have strong opinions about either way so I can revert it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't understand that last statement, it looks like a bug and not a misdesign (i.e. it looks like you intended to operate on the group (g) and not on self, but you coded self)?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just ping me on Slack if you get a chance...probably quicker to resolve that way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, you are right. I misinterpreted the problem you were describing, sorry for the confusion. Will take a look and fix.

@ktbyers
Copy link
Collaborator

ktbyers commented Sep 13, 2018

I am just going to submit some items for review that I see (as opposed to trying to review the entire PR at once).

@ktbyers
Copy link
Collaborator

ktbyers commented Sep 13, 2018

What was the main reason for changing this (i.e. for changing the groups to be a list of group names)? Was this mainly to have contains behave better or some other reasons?

(Pdb) p self.groups
['nxos']
(Pdb) p self.groups.refs
[Group: nxos]

Does refs name have some meaning?

@dbarrosop
Copy link
Contributor Author

Yeah, it feels cleaner overall but it can certainly be reverted to the previous behavior. I don't recall changing that because of mypy surfacing some code smell.

@ktbyers
Copy link
Collaborator

ktbyers commented Sep 29, 2018

Okay, that all makes sense and I tested the Host object behavior and the logging behavior.

@dbarrosop
Copy link
Contributor Author

Let me know when you think this one is good to go

@ktbyers
Copy link
Collaborator

ktbyers commented Sep 30, 2018 via email

@ktbyers
Copy link
Collaborator

ktbyers commented Oct 2, 2018

+1 ... merge away.

@dbarrosop
Copy link
Contributor Author

I will probably send a PR on top of this one doing some fixes to the configuration and its documentation before mering. Otherwise it's going to be confusing for people using the 2.0 branch.

@ktbyers
Copy link
Collaborator

ktbyers commented Oct 4, 2018

@dbarrosop What if we made a 2.1 branch (i.e. branch 2.1 from 2.0) and then merge this change into 2.1.

We could then notify people in the Slack channel that 2.0 will never be released and that they need to migrate their code over to 2.1 format (and that there will be a set of things that are going to break in this process).

Then all new PRs against 2.X would need to go into the 2.1 branch.

@dbarrosop
Copy link
Contributor Author

dbarrosop commented Oct 7, 2018

We could just create a 2.0rc1 branch and deprecate 2.0 instead. I don't think there is any need to actually skip a release version. Let me work first on the mentioned PR as I mostly want to avoid changing the structure of the configuration without documenting it.

@ktbyers
Copy link
Collaborator

ktbyers commented Oct 7, 2018

Sounds good.

@dbarrosop dbarrosop mentioned this pull request Oct 14, 2018
Copy link
Collaborator

@dmfigol dmfigol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, David.
Just a couple of minor things I think we can improve.
Things I like the most about this PR:

  1. separate section for host data
  2. strict schema and this example:
from nornir.core.deserializer.inventory import InventoryElement
import json
print(json.dumps(InventoryElement.schema(), indent=4))

As a user, you can clearly see what is expected from you and it is in the tutorial!

"<span class=\"lineno\"> 4 </span> <span class=\"l l-Scalar l-Scalar-Plain\">domain</span><span class=\"p p-Indicator\">:</span> <span class=\"l l-Scalar l-Scalar-Plain\">global.local</span>\n",
"<span class=\"lineno\"> 5 </span> <span class=\"l l-Scalar l-Scalar-Plain\">asn</span><span class=\"p p-Indicator\">:</span> <span class=\"l l-Scalar l-Scalar-Plain\">1</span>\n",
"<span class=\"lineno\"> 6 </span>\n",
"<span class=\"lineno\"> 7 </span><span c
Copy link
Collaborator

@dmfigol dmfigol Oct 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section is empty.
Update, link to the line does not seem to work correctly.
This was referring to:
"You can also grab the children of a group:"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand but I am going to merge nonetheless. We need a broader effort to fix docs.

"<span class=\"lineno\"> 4 </span> <span class=\"l l-Scalar l-Scalar-Plain\">domain</span><span class=\"p p-Indicator\">:</span> <span class=\"l l-Scalar l-Scalar-Plain\">global.local</span>\n",
"<span class=\"lineno\"> 5 </span> <span class=\"l l-Scalar l-Scalar-Plain\">asn</span><span class=\"p p-Indicator\">:</span> <span class=\"l l-Scalar l-Scalar-Plain\">1</span>\n",
"<span class=\"lineno\"> 6 </span>\n",
"<span class=\"lineno\"> 7 </span><span c
Copy link
Collaborator

@dmfigol dmfigol Oct 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. typo complext
  2. shouldn't this section directly lead to the advanced filtering notebook?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixing, I hope :)


to::

from nornir import InitNornir
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add also a mention about data going to the specific section and link to an example with the new inventory?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am going to leave this out for now. We certainly need to work on the upgrade notes and the docs before the official release.

dry_run(``bool``): Whether if we are testing the changes or not
config (:obj:`nornir.core.configuration.Config`): Configuration parameters
"""

def __init__(
self, inventory, dry_run, config=None, config_file=None, logger=None, data=None
self, inventory, _config=None, config_file=None, logger=None, data=None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_config is weird for public API. Could you please clarify this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's because config is a property and there is some magic with the filter method. However, this can be fixed in #252, I just don't want to overcomplicate this PR even more.

nornir/core/deserializer/inventory.py Show resolved Hide resolved
versions of Nornir after running ``filter`` multiple times.

Attributes:
failed_hosts (list): Hosts that have failed to run a task properly
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a set, but let's remove it and rely on annotation for the set

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

nornir/core/state.py Show resolved Hide resolved
nornir/core/deserializer/inventory.py Show resolved Hide resolved
@dbarrosop dbarrosop changed the base branch from pydantic to 2.0.0-beta October 23, 2018 16:13
@dbarrosop dbarrosop merged commit e29aa1a into 2.0.0-beta Oct 23, 2018
@dbarrosop dbarrosop deleted the inventory_rework branch October 27, 2018 11:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants