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

Psutil changes in public_ips() causes breaking changes #1039

Closed
BwL1289 opened this issue Sep 20, 2024 · 8 comments · Fixed by #1040
Closed

Psutil changes in public_ips() causes breaking changes #1039

BwL1289 opened this issue Sep 20, 2024 · 8 comments · Fixed by #1040

Comments

@BwL1289
Copy link

BwL1289 commented Sep 20, 2024

Introduced in 5d4888116a69b8f8edb69e51a22b8d4df3a54741 in jupyter_client/localinterfaces.py, the changes made to public_ips() method breaks existing deployments.

Previously, the following yielded the correct public ip address:

ip = public_ips()[0]
print(ip)  # 10.0.XX.XX

Now, it yields the linked local address:

ip = public_ips()[0]
print(ip)  # 169.254.XXX.X

For now, I am manually filtering out IPs that are one of: ipv4 link local ips (unicast), ipv6 ips (unicast), ipv4 multicast ips, or ipv6 multicast ips, but would love to see this get fixed upstream.

@minrk
Copy link
Member

minrk commented Sep 21, 2024

Thanks for reporting! I'll see if I can find a quick fix. Can you share the full public ips list for the two (feel free to obfuscate part of the ips, I mainly want to see if the lists are the same and how the order differs)? The ordering hasn't ever technically been meaningful, but it's nice when a guess is useful and consistency is important.

@BwL1289
Copy link
Author

BwL1289 commented Sep 21, 2024

Unfortunately I only have the full list of public ips for the new behavior (before we filter) but here's the list:

['169.254.XXX.X', '10.0.XX.XX']

Let me know what else I can provide!

@minrk
Copy link
Member

minrk commented Sep 21, 2024

Ah, sorry. You can still compute them both if you have both netifaces and psutil:

import netifaces
import psutil
from jupyter_client import localinterfaces

localinterfaces._load_ips_psutil()
print("psutil   ", localinterfaces.PUBLIC_IPS)
localinterfaces._load_ips_netifaces()
print("netifaces", localinterfaces.PUBLIC_IPS)

print("psutil   ", list(psutil.net_if_addrs()))
print("netifaces", list(netifaces.interfaces()))

which give me:

psutil    ['192.168.1.50', '192.168.69.1']
netifaces ['192.168.1.50', '192.168.69.1']
psutil    ['lo0', 'en0', 'bridge100', 'anpi0', 'anpi1', 'anpi2', 'en4', 'en5', 'en6', 'en1', 'en2', 'en3', 'bridge0', 'ap1', 'awdl0', 'llw0', 'vmenet0', 'utun0', 'utun1', 'utun2', 'utun3', 'utun4', 'utun5', 'utun6', 'utun7']
netifaces ['lo0', 'gif0', 'stf0', 'anpi0', 'anpi1', 'anpi2', 'en4', 'en5', 'en6', 'en1', 'en2', 'en3', 'bridge0', 'ap1', 'en0', 'awdl0', 'llw0', 'utun0', 'utun1', 'utun2', 'utun3', 'utun4', 'utun5', 'utun6', 'utun7', 'vmenet0', 'bridge100']

and if you can identify the interface label for each of the ipv4 addresses, e.g. with:

ifconfig | grep -B 5 'inet '

But public_ips should effectively be considered an unordered set. We can manually sort 169.254 to last.

@BwL1289
Copy link
Author

BwL1289 commented Sep 21, 2024

Got it! I can keep the filtering we have.

Manually sorting link locals to last or updating the docs that it should be considered an unordered set sounds like a good solution. Might help some users down the road.

Thanks!

@minrk
Copy link
Member

minrk commented Sep 21, 2024

Thinking about it, 169.254 probably should be filtered out of public_ips like 127 is. I can't think of what that change would break, but probably something.

@BwL1289
Copy link
Author

BwL1289 commented Sep 21, 2024

Makes sense to me (though not sure about the unintended breaking changes that may result). The method name public_ips makes me think this is the behavior users expect.

There are only two hard things in Computer Science: cache invalidation and naming things. -- Phil Karlton

@minrk
Copy link
Member

minrk commented Sep 21, 2024

#1040 removes link-local addresses from public_ips entirely. public_ips is specifically meant to be for "pick some ips that other machines on some network will be able to connect to me", and link-local isn't that, so it makes sense to exclude it just like we do for 127.

BTW, how were you using this? I've used public_ips()[0] in some jupyterhub demos, but it's never been guaranteed to work since ordering is unstable and not consistent across systems, and the right answer depends on your networking situation. Specifying an actual network interface is far more reliable (but tedious, because every environment has different networking interfaces):

import socket
import psutil


def ip_for_iface(iface="en0"):
    """Return ipv4 address for the given named network interface"""
    # select interface
    iface_addrs = psutil.net_if_addrs()[iface]
    # filter to ipv4
    iface_inet = [addr for addr in iface_addrs if addr.family == socket.AF_INET]
    # return first ip
    return iface_inet[0].address


print(ip_for_iface("en0"))

@BwL1289
Copy link
Author

BwL1289 commented Sep 22, 2024

This was a naive implementation in dev.

Deploying to prod will include specifying a network interface as you indicated above.

Thank you and appreciate the prompt patch.

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 a pull request may close this issue.

2 participants