102

When an error occurs in a Python script on Unix, an email is sent.

I have been asked to add {Testing Environment} to the subject line of the email if the IP address is 192.168.100.37 which is the testing server. This way we can have one version of a script and a way to tell if the email is coming from messed up data on the testing server.

However, when I google I keep finding this code:

import socket
socket.gethostbyname(socket.gethostname())

However, that's giving me the IP address of 127.0.1.1. When I use ifconfig I get this

eth0      Link encap:Ethernet  HWaddr 00:1c:c4:2c:c8:3e
          inet addr:192.168.100.37  Bcast:192.168.100.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:75760697 errors:0 dropped:411180 overruns:0 frame:0
          TX packets:23166399 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:59525958247 (59.5 GB)  TX bytes:10142130096 (10.1 GB)
          Interrupt:19 Memory:f0500000-f0520000

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:25573544 errors:0 dropped:0 overruns:0 frame:0
          TX packets:25573544 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:44531490070 (44.5 GB)  TX bytes:44531490070 (44.5 GB)

Firstly, I don't know where it got 127.0.1.1 from, but either way that's not what I want. When I google I keep coming to the same syntax, Bash scripts or netifaces and I'm trying to use standard libraries.

So how can I get the IP address of eth0 in Python?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Memor-X
  • 2,870
  • 6
  • 33
  • 57
  • "I don't know where it got 127.0.1.1 from" from the `/etc/hosts` file. – Ivan De Paz Centeno Jul 15 '16 at 11:07
  • You mentioned, "`127.0.1.1`". Did you mean, "`127.0.0.1`"? Because that is your local loopback interface, `lo` (the second entry in your `ifconfig` output); and the first entry if you `cat /etc/hosts`. – voices Apr 19 '17 at 12:34

15 Answers15

225

Two methods:

Method #1 (use external package)

You need to ask for the IP address that is bound to your eth0 interface. This is available from the netifaces package

import netifaces as ni
ip = ni.ifaddresses('eth0')[ni.AF_INET][0]['addr']
print(ip)  # should print "192.168.100.37"

You can also get a list of all available interfaces via

ni.interfaces()

Method #2 (no external package)

Here's a way to get the IP address without using a python package:

import socket
import fcntl
import struct

def get_ip_address(ifname):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    return socket.inet_ntoa(fcntl.ioctl(
        s.fileno(),
        0x8915,  # SIOCGIFADDR
        struct.pack('256s', ifname[:15])
    )[20:24])

get_ip_address('eth0')  # '192.168.0.110'

Note: detecting the IP address to determine what environment you are using is quite a hack. Almost all frameworks provide a very simple way to set/modify an environment variable to indicate the current environment. Try and take a look at your documentation for this. It should be as simple as doing

if app.config['ENV'] == 'production':
  # send production email
else:
  # send development email
lwei
  • 55
  • 2
  • 6
Martin Konecny
  • 57,827
  • 19
  • 139
  • 159
  • 1
    is netifaces a standard library? – Memor-X Jun 13 '14 at 02:50
  • 1
    It's not in the standard library, but he added the link – jgritty Jun 13 '14 at 02:52
  • Unfortunately not. If you don't want to use this package you can parse the output of `ifconfig` for the interface that you want. – Martin Konecny Jun 13 '14 at 02:52
  • Yes exactly - it's not as desirable. BTW updated my answer since I don't believe you are approaching this the right way. – Martin Konecny Jun 13 '14 at 02:56
  • @MartinKonecny it's the approach that's been detailed in the task assigned to me but i'll take your advice on board and speak to the person requesting the functionality (my experience with python/unix is basic, i'm just modifying existing scripts) – Memor-X Jun 13 '14 at 03:08
  • Are you using a framework being used for the project? Is it the server that's sending the email, or is it a background workker/cron job etc. – Martin Konecny Jun 13 '14 at 03:10
  • @MartinKonecny cron job calls a bash script which calls the python script however this python script is used in a number of different bash scripts, i could add an argument to the python script which whcih accepts the ip address but that means editing every bash script and some are a bit more complex. ideally i want to avoid that – Memor-X Jun 13 '14 at 03:14
  • Do you have a dedicated dev server (not shared with production)? It might be easier to just check the hostname from within python. The hostname of a dev server and production server should be different: `import socket; print(socket.gethostname())` – Martin Konecny Jun 13 '14 at 03:24
  • @MartinKonecny not entire sure but i spoke with the person who requested the functionality, they have agreed to go with the environment variable (because they don't want to install another library just to get the ip for something small like this) since we already use those for database connection criteria, thanks for your help – Memor-X Jun 13 '14 at 03:30
  • 5
    I know I'm late to the party but could someone explain the sorcery that is Edit 2? I'm new to the socket library and have never used the other two. – Austin A Jul 08 '15 at 03:19
  • 3
    Why ifname[:15] instead of just ifname? – bdrx Sep 24 '15 at 15:35
  • 1
    Python 2,.7 `ImportError: No module named fcntl` – Mawg says reinstate Monica Mar 18 '16 at 10:54
  • 44
    For Python 3, ifname[:15] should be bytes(ifname[:15], 'utf-8') – Taranjeet Jun 23 '16 at 09:53
  • 15
    Given that it's Python, importing netifaces as ni is particularly appropriate... for some knights at least. ;-) – Ubuntourist Jul 08 '16 at 14:42
  • 2
    @AustinA [`ioctl`](http://man7.org/linux/man-pages/man2/ioctl.2.html) is a common way to perform operations on file descriptors that don't fit the common `read` / `write` semantics. Because there are a [ton of different operations](http://man7.org/linux/man-pages/man2/ioctl_list.2.html) one might perform, `ioctl` takes a code indicating the operation. Here, `SIOCGIFADDR` means "**s**ocket **ioc**tl **g**et **i**nter**f**ace **addr**ess". – Jonathon Reinhart Aug 18 '16 at 20:15
  • 2
    @AustinA `SIOCGIFADDR` receives/returns information via a [`struct ifreq` ](http://man7.org/linux/man-pages/man7/netdevice.7.html) structure. This very brief code is taking advantage of the fact that the interface name is at offset zero in that structure, and just passing a big padded blob to the kernel. Then, it reads the 32-bit address out of the `struct sockaddr` at offset 20. This could be written much clearly, but this gets the job done. – Jonathon Reinhart Aug 18 '16 at 20:18
  • Number 2 is not portable. It doesn't work on OpenBSD and probably doesn't on FreeBSD/pfSense either. – Yet Another User Jan 30 '17 at 01:30
  • 1
    A couple of other notes. This will only find IPV4 addresses, and only the first one – meawoppl Jan 06 '19 at 17:29
  • 3
    Method 2 leaks a socket every time it's called. You need to call s.close() in a finally block. – hallidave Mar 05 '19 at 20:02
  • 2
    Method # 2 gives error `OSError: [Errno 6] Device not configured` MAC (Darwin Kernel Version 19.6.0: root:xnu-6153.141.1~1/RELEASE_X86_64 x86_64) – ViFI Sep 24 '20 at 19:54
  • `fcntl` is not available on Windows – Elliott B Jun 23 '22 at 04:30
  • how to get interface name from ip using the fcntl method ? @MartinKonecny – Anand Feb 17 '23 at 05:32
157

Alternatively, if you want to get the IP address of whichever interface is used to connect to the network without having to know its name, you can use this:

import socket
def get_ip_address():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect(("8.8.8.8", 80))
    return s.getsockname()[0]

I know it's a little different than your question, but others may arrive here and find this one more useful. You do not have to have a route to 8.8.8.8 to use this. All it is doing is opening a socket, but not sending any data.

jeremyjjbrown
  • 7,772
  • 5
  • 43
  • 55
  • 4
    Yep, probably not going anywhere but some LANs may not be exposed to it. – jeremyjjbrown Jul 06 '15 at 13:25
  • 1
    Is closing of the socket here unnecessary? – krizajb Oct 18 '17 at 09:20
  • 1
    @krizajb I've never bothered since I never do it more than once per pid and the single file descriptor would get cleaned up with the pid if it's not realeased during gc. If for some reason you need to do it a lot, then using a context manager would be pythonic https://stackoverflow.com/questions/16772465/ – jeremyjjbrown Oct 18 '17 at 20:41
  • I don't understand the purpose of connect to 8.8.8.8 – k4ppa Nov 23 '17 at 17:13
  • 1
    @k4ppa The connect to 8.8.8.8 forces the system to make an external connection which resolves the full route. This, in turn, provides the physical network-facing (or one of the network-facing) IP numbers for this system (based on whichever adapter the kernel selected for this connection). Without the connect you will get 0.0.0.0 for the socket name. There is no magic in 8.8.8.8 other than that it is (likely to be) always there and certainly able to handle the traffic. On a non-internet connected private network you would use some server number (or name) on that network. – Steve Cohen Oct 05 '18 at 16:48
  • @SteveCohen it works if you have no route to the IP. I've tested that. – jeremyjjbrown Oct 08 '18 at 16:38
  • There's one flaw I'm seeing on MacOS with this approach; when using a VPN it returns the VPN-assigned IP address rather than my LAN IP address. – Dean Wampler Mar 07 '20 at 17:18
  • @DeanWampler probably because that's where your traffic is going. – jeremyjjbrown Mar 20 '20 at 18:57
  • this can lead to security issues like this: https://nvd.nist.gov/vuln/detail/CVE-2019-14511 – giuliano-oliveira Aug 01 '20 at 21:32
  • 1
    @llpinokio This is just a way to get the IP. That's not a security issue that relates to listening to all IPs without authorization. They are Orthogonal. – jeremyjjbrown Aug 05 '20 at 19:24
  • It also works when pointing to your gateway (e.g. 192.168.0.1 or 192.168.178.1) , which does not expose anything to the internet. – Lau Jan 07 '21 at 09:39
  • Can I get the current device name, e.g. `eth0`, also?`getsockname()` is a [tuple](https://stackoverflow.com/a/44454064/1705829) with 2 items, first one the ip and second the port. – Timo May 05 '21 at 18:38
37

A simple approach which returns a string with IP addresses for the interfaces is:

from subprocess import check_output

ips = check_output(['hostname', '--all-ip-addresses'])

For more information, see hostname.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Alexander
  • 541
  • 5
  • 13
  • I like this one specifically as it does provide the correct IP of a docker container while being inside the container. Otherwise one would go for the first one being reported. Wonderful! – Guido U. Draheim Jan 27 '18 at 14:41
  • 4
    This is specific to the Linux net-tools version of `hostname`. The Mac/BSD hostname command does not provide this functionality. If you're using the Linux version, this probably is the easiest way to do it. – theferrit32 Mar 20 '18 at 20:46
22

Since most of the answers use ifconfig to extract the IPv4 address from the eth0 interface, which is deprecated on most Linux distros in favor of ip addr, the following code could be used instead:

import os

ipv4 = os.popen('ip addr show eth0 | grep "\<inet\>" | awk \'{ print $2 }\' | awk -F "/" \'{ print $1 }\'').read().strip()
ipv6 = os.popen('ip addr show eth0 | grep "\<inet6\>" | awk \'{ print $2 }\' | awk -F "/" \'{ print $1 }\'').read().strip()

Alternatively, you can shift part of the parsing task to the Python interpreter by using split() instead of grep and AWK, as Sergiy Kolodyazhnyy points out in the comment:

import os

ipv4 = os.popen('ip addr show eth0').read().split("inet ")[1].split("/")[0]
ipv6 = os.popen('ip addr show eth0').read().split("inet6 ")[1].split("/")[0]

But in this case you have to check the bounds of the array returned by each split() call.


Another version using regex:

import os
import re

ipv4 = re.search(re.compile(r'(?<=inet )(.*)(?=\/)', re.M), os.popen('ip addr show eth0').read()).groups()[0]
ipv6 = re.search(re.compile(r'(?<=inet6 )(.*)(?=\/)', re.M), os.popen('ip addr show eth0').read()).groups()[0]
Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
Ivan De Paz Centeno
  • 3,595
  • 1
  • 18
  • 20
  • 3
    good job of using `ip addr` but , it really isn't necessary to pipe output to grep and awk. Python is more than capable of parsing the output with split() – Sergiy Kolodyazhnyy Sep 01 '16 at 00:11
  • To make it a little more machine-parsable, try the `-oneline` option. `"output each record on a single line, replacing line feeds with the '\' character. This is convenient when you want to count records with wc(1) or to grep(1) the output."` – CivFan Mar 16 '17 at 16:46
  • 1
    In addition, newer releases of `iproute2` (of which `ip` is a part) support `-j, --json` argument for most commands that does what you'd expect. – iamkroot Feb 02 '20 at 17:24
17

If you only need to work on Unix, you can use a system call (ref. Stack Overflow question Parse ifconfig to get only my IP address using Bash):

import os
f = os.popen('ifconfig eth0 | grep "inet\ addr" | cut -d: -f2 | cut -d" " -f1')
your_ip=f.read()
Community
  • 1
  • 1
olmerg
  • 425
  • 4
  • 6
  • 2
    This one is specific to the distribution. For example on RHEL, the cut commands cuts off the wrong term. So this one isn't portable across *nix distributions. – Eric Leschinski Jul 14 '16 at 14:54
  • 2
    `grep` and `cut` are really not necessary here, just get the output and parse it. Also, `ip addr` command may be much more preferred. – Sergiy Kolodyazhnyy Sep 01 '16 at 00:10
5

This will gather all IP addresses on the host and filter out loopback/link-local and IPv6. This can also be edited to allow for IPv6 only, or both IPv4 and IPv6, as well as allowing loopback/link-local in the IP address list.

from socket import getaddrinfo, gethostname
import ipaddress

def get_ip(ip_addr_proto="ipv4", ignore_local_ips=True):
    # By default, this method only returns non-local IPv4 addresses
    # To return IPv6 only, call get_ip('ipv6')
    # To return both IPv4 and IPv6, call get_ip('both')
    # To return local IPs, call get_ip(None, False)
    # Can combine options like so get_ip('both', False)

    af_inet = 2
    if ip_addr_proto == "ipv6":
        af_inet = 30
    elif ip_addr_proto == "both":
        af_inet = 0

    system_ip_list = getaddrinfo(gethostname(), None, af_inet, 1, 0)
    ip_list = []

    for ip in system_ip_list:
        ip = ip[4][0]

        try:
            ipaddress.ip_address(str(ip))
            ip_address_valid = True
        except ValueError:
            ip_address_valid = False
        else:
            if ipaddress.ip_address(ip).is_loopback and ignore_local_ips or ipaddress.ip_address(ip).is_link_local and ignore_local_ips:
                pass
            elif ip_address_valid:
                ip_list.append(ip)

    return ip_list

print(f"Your IP address is: {get_ip()}")

Returns

Your IP address is: ['192.168.1.118']

If I run get_ip('both', False), it returns

Your IP address is: ['::1', 'fe80::1', '127.0.0.1', '192.168.1.118', 'fe80::cb9:d2dd:a505:423a']

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Geruta
  • 151
  • 1
  • 1
4

Building on the answer from jeremyjjbrown, here is another version that cleans up after itself as mentioned in the comments to his answer.

This version also allows providing a different server address for use on private internal networks, etc.

import socket

def get_my_ip_address(remote_server="google.com"):
    """
    Return the/a network-facing IP number for this system.
    """
    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
        s.connect((remote_server, 80))
        return s.getsockname()[0]
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Steve Cohen
  • 722
  • 4
  • 6
  • 1
    I would suggest using an IP address rather than a hostname as the default value for "remote_server", so you don't trigger a DNS lookup. – plugwash Dec 11 '20 at 21:18
3

Answer using psutil:

import psutil
import socket

def get_ipv4_from_nic(interface):
    interface_addrs = psutil.net_if_addrs().get(interface) or []
    for snicaddr in interface_addrs:
        if snicaddr.family == socket.AF_INET:
            return snicaddr.address

Example:

>>> get_ipv4_from_nic("eth0")
'192.168.100.37'
ofirule
  • 4,233
  • 2
  • 26
  • 40
1

If you want to do it the hard (but maybe fast?) way, here's some rough Netlink (RFC 3549) code (probably Linux only) which gets both IPv4 and IPv6, with just one import statement from the standard library:

    import socket
    # https://www.man7.org/linux/man-pages/man7/rtnetlink.7.html
    # https://github.com/torvalds/linux/blob/master/include/uapi/linux/rtnetlink.h
    RTM_NEWADDR = 20
    RTM_GETADDR = 22
    # https://www.man7.org/linux/man-pages/man7/netlink.7.html
    # https://github.com/torvalds/linux/blob/master/include/uapi/linux/netlink.h
    NLM_F_REQUEST = 0x01
    NLM_F_ROOT = 0x100
    s = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW)
    req = (
        # nlmsghdr
        int.to_bytes(0, 4, 'little', signed=False) +  # nlmsg_len
        int.to_bytes(RTM_GETADDR, 2, 'little', signed=False) +  # nlmsg_type
        int.to_bytes(NLM_F_REQUEST | NLM_F_ROOT, 2, 'little', signed=False) +  # nlmsg_flags
        int.to_bytes(0, 2, 'little', signed=False) +  # nlmsg_seq
        int.to_bytes(0, 2, 'little', signed=False) +  # nlmsg_pid
        # ifinfomsg
        b'\0' * 8
    )
    req = int.to_bytes(len(req), 4, 'little') + req[4:]
    s.sendall(req)
    full_resp = s.recv(4096)
    while full_resp:
        resp = full_resp
        # nlmsghdr
        nlmsg_len = int.from_bytes(resp[0:4], 'little', signed=False)
        nlmsg_type = int.from_bytes(resp[4:6], 'little', signed=False)
        assert not nlmsg_len % 4, nlmsg_len
        resp = resp[16:nlmsg_len]
        full_resp = full_resp[nlmsg_len:]
        if nlmsg_type == 3:  # NLMSG_DONE
            assert not full_resp, full_resp
            break
        if not full_resp:
            full_resp = s.recv(4096)
        assert nlmsg_type == RTM_NEWADDR, (nlmsg_type, resp[:32])
        # ifaddrmsg
        ifa_family = int.from_bytes(resp[0:1], 'little', signed=False)
        ifa_index = int.from_bytes(resp[4:8], 'little', signed=False)
        resp = resp[8:]
        while resp:
            # rtattr
            rta_len = int.from_bytes(resp[0:2], 'little', signed=False)
            rta_type = int.from_bytes(resp[2:4], 'little', signed=False)
            data = resp[4:rta_len]
            if rta_type == 1:  # IFLA_ADDRESS
                if ifa_family == socket.AF_INET:
                    ip = '.'.join('%d' % c for c in data)
                if ifa_family == socket.AF_INET6:
                    ip = ':'.join(('%02x%02x' % (chunk[0], chunk[1]) if chunk != b'\0\0' else '') for chunk in [data[0:2], data[2:4], data[4:6], data[6:8], data[8:10], data[10:12], data[12:14], data[14:16]])
                print('interface #%s has %s' % (ifa_index, ip))
            if rta_type == 3:  # IFLA_IFNAME
                ifname = data.rstrip(b'\0').decode()
                print('interface #%s is named %s' % (ifa_index, ifname))
            # need to round up to multiple of 4
            if rta_len % 4:
                rta_len += 4 - rta_len % 4
            resp = resp[rta_len:]
    s.close()

If you just need IPv4, the old school SIOCGIFADDR ioctl method in another answer is probably more straightforward. For IPv6, there's /proc/net/if_inet6.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Collin Anderson
  • 14,787
  • 6
  • 68
  • 57
0

Try the below code. It works for me in Mac OS X v10.10.2 (Yosemite):

import subprocess

if __name__ == "__main__":
    result = subprocess.check_output('ifconfig en0 |grep -w inet', shell=True) # you may need to use eth0 instead of en0 here!!!
    print 'output = %s' % result.strip()
    # result = None
    ip = ''
    if result:
        strs = result.split('\n')
        for line in strs:
            # remove \t, space...
            line = line.strip()
            if line.startswith('inet '):
                a = line.find(' ')
                ipStart = a+1
                ipEnd = line.find(' ', ipStart)
                if a != -1 and ipEnd != -1:
                    ip = line[ipStart:ipEnd]
                    break
    print 'ip = %s' % ip
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
xiaoweiz
  • 162
  • 3
  • 10
0

It worked for me

 import subprocess
 my_ip = subprocess.Popen(['ifconfig eth0 | awk "/inet /" | cut -d":" -f 2 | cut -d" " -f1'], stdout=subprocess.PIPE, shell=True)
 (IP,errors) = my_ip.communicate()
 my_ip.stdout.close()
 print IP
Abhijit
  • 631
  • 7
  • 13
  • 1
    My guess is that someone -1'ed this because Linux doesn't use a ":" separator in the ifconfig output. This command will only work on some Unix which does include such a thing. – Mike S May 23 '17 at 22:35
0

This is the result of ifconfig:

ifconfig

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.2.24  netmask 255.255.255.0  broadcast 192.168.2.255
        inet6 fe80::88e9:4d2:c057:2d5f  prefixlen 64  scopeid 0x20<link>
        ether b8:27:eb:d0:9a:f3  txqueuelen 1000  (Ethernet)
        RX packets 261861  bytes 250818555 (239.1 MiB)
        RX errors 0  dropped 6  overruns 0  frame 0
        TX packets 299436  bytes 280053853 (267.0 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 74  bytes 16073 (15.6 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 74  bytes 16073 (15.6 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

wlan0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        ether b8:27:eb:85:cf:a6  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Cutting a bit the output, we have:

ifconfig eth0 | grep "inet 192" | cut -c 14-25

192.168.2.24

Now, we can go to Python and do:

import os
mine = os.popen('ifconfig eth0 | grep "inet 192" | cut -c 14-25')
myip = mine.read()
print (myip)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Omar Cruz
  • 46
  • 1
  • 7
  • I get the docker0 interface IP address using the following `docker_ip = os.popen('ifconfig docker0 | grep inet | awk \'{print $2}\'').read()` – Sajjad Ahmad Jul 20 '22 at 08:57
0

Yet another way of obtaining the IP address from a NIC, using Python.

I had this as part of an application that I developed long time ago, and I didn't wanted to simply git rm script.py. So, here I provide the approach, using subprocess and list comprehensions for the sake of functional approach and fewer lines of code:

import subprocess as sp

__version__ = "v1.0"
__author__ = "@ivanleoncz"

def get_nic_ipv4(nic):
    """
        Get IP address from a NIC.

        Parameter
        ---------
        nic : str
            Network Interface Card used for the query.

        Returns
        -------
        ipaddr : str
            Ipaddress from the NIC provided as parameter.
    """
    result = None
    try:
        result = sp.check_output(["ip", "-4", "addr", "show", nic],
                                                  stderr=sp.STDOUT)
    except Exception:
        return "Unkown NIC: %s" % nic
    result = result.decode().splitlines()
    ipaddr = [l.split()[1].split('/')[0] for l in result if "inet" in l]
    return ipaddr[0]

Additionally, you can use a similar approach for obtaining a list of NICs:

def get_nics():
    """
        Get all NICs from the Operating System.

        Returns
        -------
        nics : list
            All Network Interface Cards.
    """
    result = sp.check_output(["ip", "addr", "show"])
    result = result.decode().splitlines()
    nics = [l.split()[1].strip(':') for l in result if l[0].isdigit()]
    return nics

Here's the solution as a Gist.

And you would have something like this:

python3

Python 3.6.7 (default, Oct 22 2018, 11:32:17)
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>>
>>> import helpers
>>>
>>> helpers.get_nics()
['lo', 'enp1s0', 'wlp2s0', 'docker0']
>>> helpers.get_nic_ipv4('docker0')
'172.17.0.1'
>>> helpers.get_nic_ipv4('docker2')
'Unkown NIC: docker2'
Samveen
  • 3,482
  • 35
  • 52
ivanleoncz
  • 9,070
  • 7
  • 57
  • 49
0

You can try this code snippet:

import netifaces as ni

def test_network():
    interfaces = ni.interfaces()

    for i in interfaces: #Will cycle through all available interfaces and check each one.
        if i != "lo": #This will remove lo from the interfaces it checks.
            try:
                ni.ifaddresses(i)
                gws = ni.gateways()
                gateway = gws['default'][ni.AF_INET][0]
                ip = ni.ifaddresses(i)[ni.AF_INET][0]['addr']
                sm = ni.ifaddresses(i)[ni.AF_INET][0]['netmask']
                print ("Network information for " + i + ":")
                print ("IP address: " + ip)
                print ("Subnet Mask: " + sm)
                print ("Gateway: " + gateway)
                print ()
            except: #Error case for a disconnected Wi-Fi or trying to test a network with no DHCP
                print (i + " is not connected or DHCP is not available. Try setting a static IP address.")
test_network()

This may produce the following result:

Network information for eth0:

IP address: 192.168.1.172

Subnet Mask: 255.255.255.0

Gateway: 192.168.1.254

wlan0 is not connected or DHCP is not available.
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
-1

Find the IP address of the first eth/wlan entry in ifconfig that's running:

import itertools
import os
import re

def get_ip():
    f = os.popen('ifconfig')
    for iface in [' '.join(i) for i in iter(lambda: list(itertools.takewhile(lambda l: not l.isspace(), f)), [])]:
        if re.findall('^(eth|wlan)[0-9]', iface) and re.findall('RUNNING', iface):
            ip = re.findall('(?<=inet\saddr:)[0-9\.]+', iface)
            if ip:
                return ip[0]
    return False
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131