nornir-automation / nornir

Pluggable multi-threaded framework with inventory management to help operate collections of devices
https://nornir.readthedocs.io/
Apache License 2.0
1.4k stars 237 forks source link

added grammar parser for F object #603

Closed dbarrosop closed 1 year ago

dbarrosop commented 4 years ago

Implemented a grammar parser that turns a string into an F object. For instance:

and long etc.

Look at the tests for more examples.

TODO:

  1. Document
  2. Get feedback
  3. ~Fix CI error which is unrelated to the PR but just your usual python dependency hell~ (thanks to @ktbyers)
dmulyalin commented 3 years ago

Would be interested to hear what is the problem we trying to solve here?

Seems interesting solution, but struggling to come out with use case for it, that grammar seems quiet human readable as, IMHO, this:

1: f = parse("(site == 'site2' OR role == 'www') AND my_var == 'comes_from_dev1.group_1'")

easier to read/comprehand compared to

2: f = (F(site="site2") | F(role="www")) & F(my_var="comes_from_dev1.group_1")

however, what about this form: 3:

f = parse(
    [
        {"site": "site2", "my_var": "comes_from_dev1.group_1"}, 
        {"role": "www", "my_var": "comes_from_dev1.group_1"}
    ]
)

where, for instance, list of dictionaries express OR logic between items, but AND logic within each dictionary, that way host satisfying any of the dictionaries will pass the filter, equivalent to:

f = (
    (F(site="site2", my_var="comes_from_dev1.group_1") | 
    (F(role="www", my_var="comes_from_dev1.group_1"))
)
Option Pros Cons
1 Human readable Hard to construct programmatically and need to learn grammar
2 Built into NR, already exists Relatively hard to read and remember, hard to construct programmatically
3 Human readable and easy to construct/process programmatically Need to remember implicit filtering logic, less flexible (?)

Also, to implement option 3, code might be:

def _filter_FO(nr, filter_data):
    """
    Function to filter hosts using Filter Object
    """
    ret = nr
    ret = ret.filter(F(**filter_data[0]))
    for item in filter_data[1:]:
        filtered_hosts = nr.filter(F(**item))
        ret.inventory.hosts.update(filtered_hosts.inventory.hosts)
    return ret
dbarrosop commented 3 years ago

This is for use in command lines (i.e. a —filter flag), apis, etc where you have no python.

Notice that this grammar parser doesn’t replace the F object, as a matter of fact, the grammar parser converts a string into it so without it it wouldn’t work :) The recommended way of filtering within python will still be using the F object but everybody seems to be inventing some way of describing F objects in text somehow for their command line tools so this is the response to that.

dmulyalin commented 3 years ago

Ic, makes more sense now, thanks for explanation.

aedwardstx commented 2 years ago

I love the idea of having a standard filter grammar parser. Here's my 2 cents:

Some examples considering the above comments: