How to pass dynamic filter strings from cli?

Is there any project already using CLI arguments for Nornir?

What do you mean? Can you provide an example about what you are trying to achieve?

Are you looking for something like argsparse?

yes exactly, for example if I want to backup config only specific sites and specific roles from netbox, I want to pass that value from the cli, so I wouldn’t change the code everytime.

something would be like

python nornir_customtask.py --site="San Jose" role="core_devices"

I believe argsparse could be a solution then. You could use it to pass the arguments you want, check them and then pass them into arguments of your nornir task.

As others have pointed out argparse is great. So is click. There are many different ways to approach cli filters. I had been experimenting with it recently and came up with four examples.

https://raw.githubusercontent.com/no-such-anthony/nornir3_play/master/click-filter-1.py
https://raw.githubusercontent.com/no-such-anthony/nornir3_play/master/click-filter-2.py
https://raw.githubusercontent.com/no-such-anthony/nornir3_play/master/click-filter-3.py
https://raw.githubusercontent.com/no-such-anthony/nornir3_play/master/click-filter-4.py

-1 and -2 uses the Nornir F filter
-3 and -4 uses a custom filter

-1 and -3 are single key with multiple value search
-2 and -4 are multiple key:value search, and similiar to what you asked for. I would recommend starting with -2, as that was suggested in Nornir slack channel.

-2 is just

#-f k1:v1 -f k2:v2
fd = {}
for a in filt:
    k, v = a.split(':')
    fd[k] = v
f = F(**fd)
nr = nr.filter(f)
1 Like

the filter can catch site or role which is directly accessible but why not tags? even though tags are directly accessible as well?

tags should be fine. what are you trying to match on and what do they look like in your inventory files?

Basically I was trying to pass tag values from cli to pynetbox and I kinda achieved it by:

    nb_url= 'https://netbox.mydomain.com'
    nb_token= os.environ["NORNIR_TOKEN"]
    nb = pynetbox.api(url=nb_url,token=nb_token)
    nb.http_session.verify = False

    fd = {}
    smartnet_devices=[]
    for a in filt:
        k, v = a.split(':')
        fd[k] = v
    #ff = F(**fd)
    
    #print(type(ff))
        new_devices = nb.dcim.devices.filter(tag=[f"{v}"])
        smartnet_devices.extend(new_devices)
        #import ipdb ; ipdb.set_trace()
    my_device_list = list(set(smartnet_devices))    
    nr = nr.filter(F(name__any= [f"{items}" for items in my_device_list]))

now my questions how to differentiate obj vs strings when I pass the filter.
what is obj in here?


def regF_and(host, filt):


    def _findvalue(obj, key):
        keys = key.split('.', 1)

In that code of mine, obj is either the host or a dictionary nested within the hosts data values, hence the ‘.’ which helps with any recursion. Like the F Filter uses __

There is a new grammar parser in Nornir that might help when it is merged, added grammar parser for F object by dbarrosop · Pull Request #603 · nornir-automation/nornir · GitHub

It sounds like you need to match on any matching value from an array of values rather than an individual value? You could use the click-filter-4 filter with "-f tag:“tag1|tag2” but as a fan of keep it simple, I would suggest updating your existing code to support __any. I hope I’ve understood the problem.

    # -f name:host4 -f <data_key>:home
    # -f hostname:192.168.0.1
    # -f groups__contains:wilma -f site:barney
    # -f <data_key1>:wilma -f <data_key2>:10
    # -f <data_key>__<nested_data_key>:fred
    # -f "tag__any:wilma, fred"
    # -f tag__any:wilma,fred
    fd = {}
    for a in filt:
        k, v = a.split(':')
        if k.endswith('__any'):
            v = [x.strip() for x in v.split(',')]
        fd[k] = v
    ff = F(**fd)
    nr = nr.filter(ff)

The code above likely won’t help you. After taking another quick look I stumbled across these links

[FEATURE REQUEST] When using netbox inventory plugin, ability to filter based on device tags. · Issue #433 · nornir-automation/nornir (github.com)

How to use filter function on this inventory plugin? · Issue #11 · wvandeun/nornir_netbox (github.com)

filter parameter as list of dictionaries · Issue #23 · wvandeun/nornir_netbox (github.com)

Usage - nornir_netbox (nornir-netbox.readthedocs.io)

Just taking a guess as I’ve never used netbox, maybe something like

@click.command()
@click.option('--filt', '-f', multiple=True)
def main(filt):
    fp = {}
    for a in filt:
        k, v = a.split(':', 1)
        if k.endswith('__any'):
            k = k.rstrip('__any')
            v = [x.strip() for x in v.split(',')]
        fp[k] = v
    nr = InitNornir(
        inventory={
            "plugin": NetBoxInventory2,
            "options": {
                "nb_url": "http://netbox.local:8000",
                "nb_token": "1234567890",
                "filter_parameters": fp
            }
        }
    )

    print(nr.inventory.hosts)

I know that wasn’t pynetbox but I hope it helps you.

thanks so much,yeap that is the exact issue. I had to create two click functions, one for netbox and one for pynetbox(since tag is only available in pynetbox)