Asynchronous API

114 views
Skip to first unread message

ian coolidge

unread,
Mar 7, 2014, 4:38:41 AM3/7/14
to pyrout...@googlegroups.com
Hi,

I have been looking at your source for a bit tonight. Maybe I missed it, but I am looking for an asynchronous netlink event API.
That is, I have a thread, like, an io loop (from tornado maybe), and I want a way to have a function called whenever netlink route data is broadcast unsolicited from the kernel.

Are there plans for this?
I may be able to help.

I suppose that at a minimum, I could grab the FD or socket object from the IO thread that pyroute2 constructs, and do a synchronous query after POLLIN is positive for it.

Thanks!
Ian

Peter Saveliev

unread,
Mar 7, 2014, 6:08:00 AM3/7/14
to ian coolidge, pyrout...@googlegroups.com
There are two ways already:

1. pyroute2/iocore/loop.py, IOLoop.register()

The most low level. You can inspect how works IPRoute object with IPRoute.ioloop (see pyroute2/iocore/iocore.py). This way you can intercept any event on any FD in all pyroute2 framework on all levels.

2. (I would recommend that)

Callbacks:

```
ipr = IPRoute()
ipr.register_callback( <callback>, <predicate>, args)
ipr.monitor()
```

Example: tests/test_ipr.py:190 [1]
Definition: pyroute2/iocore/iocore.py:346 [2]

The callback will be called every time an event arrives. If you provide a predicate, it can be used to filter events, but basically you can do it with «if … » statement within the callback.

This code was written before the architecture was drastically changed, so it can and should be optimized. But now it works in some sense, at least.

3. You can also fine-tune packet filtering by something like u32filter, see subscription in iocore [3][4][5], but can be tricky.


If there will be any issues — pls ping me, will react asap. If any suggestions and so on — welcome the code :)

Thanks!


--
You received this message because you are subscribed to the Google Groups "pyroute2-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pyroute2-dev...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Peter Saveliev

unread,
Mar 7, 2014, 6:10:23 AM3/7/14
to ian coolidge, pyrout...@googlegroups.com
Pls note, that in order to catch broadcast events, you should call ipr.monitor(), otherwise you will catch only responses to your messages.

Thanks.

ian coolidge

unread,
Mar 8, 2014, 4:52:33 AM3/8/14
to pyrout...@googlegroups.com, ian coolidge
Peter,

Thanks a lot for your help and responses. You rock.

I tried the trivial example you out lined in the in-line documentation (simple callback without predicate that only prints msg)
but I run in to some errors on the first few messages.
Usually the first fails, but sometimes one or two succeed, only to fail on the next callback.

Traceback (most recent call last):
  File "/home/root/pyroute2/iocore/loop.py", line 34, in _dequeue
    cb(fd, data, *argv, **kwarg)
  File "/home/root/pyroute2/iocore/iocore.py", line 145, in _route
    self.parse(envelope, buf)
  File "/home/root/pyroute2/iocore/iocore.py", line 207, in parse
    self.listeners[0].put_nowait(new)
KeyError: 0

IPRoute::get_links () seems to work very well for me ordinarily, so I suspect that nothing major is wrong mechanically.
Strangely, though, on a 1Ghz ARM Cortex A8, otherwise unloaded, I see very high cpu utilization in top when doing get_links () every 2s.

I wonder if there's perhaps a mismatch between kernel netlink interface and my pyroute2. I'm using Linux 3.8, and pyroute2 0.2.6

Thanks much,

Ian

Peter Saveliev

unread,
Mar 9, 2014, 1:46:31 AM3/9/14
to ian coolidge, pyrout...@googlegroups.com

Could you pls share the code that fails, and/or try the sample attached? (you have to change the ip configuration somehow in order to init broadcast events, e.g. up/down a dummy interface)

There's nothing significant changed from the tag 0.2.6, but anyway, could you pls also try the master HEAD from the git? Just in case.

The KeyError in that place means, that broadcast events are not ignored and arrive into the IPRoute instance, but there is no default queue to enqueue them. The default 0 queue is created by the IPRoute.monitor() method, and NLMSG_DONE messages should not delete the queue under normal conditions (unlike queues for ordinary requests). So the behaviour described looks a bit interesting.

...

Very high utilization == ? I have from 3 to 5% running simple  poll get_links() every two seconds on ARM v7l.

...

No, there should be no significant mismatch, pyroute2 has almost no version-specific code, only some bridge hacks for old kernels (2.6) in IPDB, that's all.

Thanks for feedback!

cb.py

Ian Coolidge

unread,
Mar 9, 2014, 5:42:15 AM3/9/14
to Peter Saveliev, pyrout...@googlegroups.com
Peter,

Thanks for the reply.

Your example code works fine.

My code was creating an IPDB instance, and referring to the IPRoute instance. Something like:
ip = IPDB ()
ip.nl.register_callback (cb)
ip.nl.monitor ()
This seems to have problems for some reason.

I found much more success doing something like:
ip = IPDB ()
ip.nl.register_callback (cb)
ip.monitor ()

Is that sensible?

I also thought that calling monitor would block me indefinitely, but that function returns immediately.

Basically, I am writing a program that bridges some connected interfaces together. I'm trying to have it all netlink event driven.

What things am I allowed to do on the callback context?
Transactional things (like adding an interface to a bridge with add_port? I tested this and it didn't seem to work, right now I just use os.system brctl… ugly)

Btw, I am using open embedded to build my system (it's running on beagle bone black). I can send you the bit bake recipe that I create to package pyroute2 with ...

Thanks,

Ian

<cb.py>

Peter Saveliev

unread,
Mar 9, 2014, 10:44:48 AM3/9/14
to Ian Coolidge, pyrout...@googlegroups.com
There are some pitfalls in IPDB (as well as in other parts of pyroute2, but in IPDB especially).

That's, namely:

1. almost no docs
2. bad docs that exist
3. some confusing names (OK, I will try to rearrange things, but I didn't work on IPDB these days, changing only the core)

So, about routines.

1. IPRoute.monitor() just turns on the monitoring of broadcast messages, nothing more. IPRoute.monitor(False) turns it off.
2. IPDB.monitor() is a main cycle of IPDB, that is unconditionally started from __init__().

IPDB works on broadcast messages, so it turns the broadcast monitoring on anyway [here I wrote some text on callbacks, but then read the second part of your letter and realized, that the text I wrote will be of no help, since you will not be able to commit any transaction from IPRoute-driven callbacks, since they are synchronous and are executed before an event arrives to IPRoute.get()…]

...

After your letter I tried to slightly fix IPDB. Please, review the debug branch [1] (git checkout debug). Last commits brings callbacks into the main cycle of IPDB, making possible functionality like auto-bridging through normal IPDB API. The code sample is attached, it unconditionally creates «autobr» bridge and adds any new «dummy.*» interfaces to it. You can use any other conditions, but pls note `len(ipdb.autobr._transactions) == 0`, it prevents a callback run while there are any transactions on the «autobr» interface. Actually, it is needed since a port enslaving by itself causes several RTM_NEWLINK events on an enslaved port. Nothing bad will happen if you omit such condition, but it supresses unnecessary exceptions :)

It is only a proof-of-concept (it doesn't join cb threads, e.g.), but if it can work for you, I will polish it and merge into the master.

Thanks!

autobr.py

Ian Coolidge

unread,
Mar 9, 2014, 9:38:04 PM3/9/14
to Peter Saveliev, pyrout...@googlegroups.com
Peter,

This works great.

Thanks very much for your efforts!

Ian


<autobr.py>

Peter Saveliev

unread,
Mar 10, 2014, 5:04:34 AM3/10/14
to Ian Coolidge, pyrout...@googlegroups.com
So I will take this approach and do some cleanup (like thread joins, GC etc).

Code will be merged into the master branch till Wednesday.

Thanks for feedback!

Peter Saveliev

unread,
Mar 10, 2014, 12:46:45 PM3/10/14
to Ian Coolidge, pyrout...@googlegroups.com
I'm working on a testing this functionality and found that it is extremely unstable in terms of synchronization.

So right now I merge it into the master «as is» — since it is working for you. But please keep in mind that it is not working as well as it is expected.

I will slightly refactor IPDB next two weeks, splitting utility functions (create interface, register callbacks etc.) and interface references (ipdb.lo, ipdb.eth0 etc.) into separate namespaces, introducing some sync. primitives (wait for interface creation etc.). Hope, the callbacks will be fixed too. If you have any feature requests, pls don't hesitate to ping me so I will include the needed functionality. Or propose the code via github pull-requests.

Thanks!

Peter Saveliev

unread,
Mar 10, 2014, 4:06:33 PM3/10/14
to Ian Coolidge, pyrout...@googlegroups.com
Pls review the ng branch [1] (git checkout ng).

Changes:
1. all interfaces references are moved into own namespaces: ipdb.eth0 --> ipdb.interfaces.eth0; ipdb['eth0'] --> ipdb.interfaces['eth0']
2. IPDB.wait_interface(): in the simplest case, this function allows to reliably wait for interface creation (callback-based, thanks for idea :)
3. IPDB.exclusive: it is a threading.RLock objects, that allows to reliably work with IPDB from multiple threads.

The updated «autobr» example is attached. If it will work for you, I would switch to this code and tag the new release version.

Thanks!

[1] https://github.com/svinota/pyroute2/tree/ng
autobr.py

ian coolidge

unread,
Mar 11, 2014, 4:16:23 PM3/11/14
to Peter Saveliev, pyrout...@googlegroups.com
Peter,

I tested ng branch, and it still works for my usecase (after I put interface references to the interfaces namespace of course)

Please let me know when you have tagged a new version!

Thanks --- Ian
Reply all
Reply to author
Forward
0 new messages