summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2015-02-11 16:10:23 +0100
committerGiampaolo Rodola <g.rodola@gmail.com>2015-02-11 16:10:23 +0100
commit5e0e458aa09f6dc84b89cdd801671536d89e8306 (patch)
tree9a4a653cbb8967443b9e89165fa467112a144b07
parent543225c5e768aa45e00827c46002a1186ed53e66 (diff)
downloadpsutil-5e0e458aa09f6dc84b89cdd801671536d89e8306.tar.gz
#250: linux implementation
-rw-r--r--README.rst3
-rw-r--r--docs/index.rst39
-rw-r--r--psutil/__init__.py23
-rw-r--r--psutil/_common.py14
-rw-r--r--psutil/_pslinux.py15
-rw-r--r--psutil/_psutil_linux.c96
-rw-r--r--psutil/_psutil_linux.h1
-rw-r--r--test/test_memory_leaks.py3
-rw-r--r--test/test_psutil.py21
9 files changed, 212 insertions, 3 deletions
diff --git a/README.rst b/README.rst
index e3368557..dc457eb6 100644
--- a/README.rst
+++ b/README.rst
@@ -156,6 +156,9 @@ Network
snic(family=<AddressFamily.AF_INET6: 10>, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None),
snic(family=<AddressFamily.AF_PACKET: 17>, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff')]}
>>>
+ >>> psutil.net_if_stats()
+ {'eth0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500),
+ 'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536)}
Other system info
=================
diff --git a/docs/index.rst b/docs/index.rst
index f860cce7..781a216f 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -444,6 +444,30 @@ Network
*New in 3.0.0*
+.. function:: net_if_stats()
+
+ Return information about each NIC (network interface card) installed on the
+ system as a dictionary whose keys are the NIC names and value is a namedtuple
+ with the following fields:
+
+ - **isup**
+ - **duplex**
+ - **speed**
+ - **mtu**
+
+ *isup* is a boolean indicating whether the NIC is up and running, *duplex*
+ can be either :const:`NIC_DUPLEX_FULL`, :const:`NIC_DUPLEX_HALF` or
+ :const:`NIC_DUPLEX_UNKNOWN`, *speed* is the NIC speed expressed in mega bits
+ (MB), if it can't be determined (e.g. 'localhost') it will be set to ``0``,
+ *mtu* is the maximum transmission unit expressed in bytes. Example:
+
+ >>> import psutil
+ >>> psutil.net_if_stats()
+ {'eth0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500),
+ 'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536)}
+
+ *New in 3.0.0*
+
Other system info
-----------------
@@ -1252,7 +1276,7 @@ Constants
Availability: Windows
- .. versionchanged:: 3.0.0 on Python >= 3.4 thse constants are
+ .. versionchanged:: 3.0.0 on Python >= 3.4 these constants are
`enums <https://docs.python.org/3/library/enum.html#module-enum>`__
instead of a plain integer.
@@ -1316,3 +1340,16 @@ Constants
To be used in conjunction with :func:`psutil.net_if_addrs()`.
*New in 3.0.0*
+
+.. _const-duplex:
+.. data:: NIC_DUPLEX_FULL
+ NIC_DUPLEX_HALF
+ NIC_DUPLEX_UNKNOWN
+
+ Constants which identifies whether a NIC (network interface card) has full or
+ half mode speed. NIC_DUPLEX_FULL means the NIC is able to send and receive
+ data (files) simultaneously, NIC_DUPLEX_FULL means the NIC can either send or
+ receive data at a time.
+ To be used in conjunction with :func:`psutil.net_if_stats()`.
+
+ *New in 3.0.0*
diff --git a/psutil/__init__.py b/psutil/__init__.py
index 7b474c09..af2482d6 100644
--- a/psutil/__init__.py
+++ b/psutil/__init__.py
@@ -43,8 +43,7 @@ from ._common import (STATUS_RUNNING, # NOQA
STATUS_WAKING,
STATUS_LOCKED,
STATUS_IDLE, # bsd
- STATUS_WAITING, # bsd
-)
+ STATUS_WAITING) # bsd
from ._common import (CONN_ESTABLISHED,
CONN_SYN_SENT,
@@ -59,6 +58,10 @@ from ._common import (CONN_ESTABLISHED,
CONN_CLOSING,
CONN_NONE)
+from ._common import (NIC_DUPLEX_FULL, # NOQA
+ NIC_DUPLEX_HALF,
+ NIC_DUPLEX_UNKNOWN)
+
if sys.platform.startswith("linux"):
from . import _pslinux as _psplatform
from ._pslinux import (phymem_buffers, # NOQA
@@ -152,6 +155,7 @@ __all__ = [
"virtual_memory", "swap_memory", # memory
"cpu_times", "cpu_percent", "cpu_times_percent", "cpu_count", # cpu
"net_io_counters", "net_connections", "net_if_addrs", # network
+ "net_if_stats",
"disk_io_counters", "disk_partitions", "disk_usage", # disk
"users", "boot_time", # others
]
@@ -1853,6 +1857,21 @@ def net_if_addrs():
return dict(ret)
+def net_if_stats():
+ """Return information about each NIC (network interface card)
+ installed on the system as a dictionary whose keys are the
+ NIC names and value is a namedtuple with the following fields:
+
+ - isup: whether the interface is up (bool)
+ - duplex: can be either NIC_DUPLEX_FULL, NIC_DUPLEX_HALF or
+ NIC_DUPLEX_UNKNOWN
+ - speed: the NIC speed expressed in mega bits (MB); if it can't
+ be determined (e.g. 'localhost') it will be set to 0.
+ - mtu: the maximum transmission unit expressed in bytes.
+ """
+ return _psplatform.net_if_stats()
+
+
# =====================================================================
# --- other system related functions
# =====================================================================
diff --git a/psutil/_common.py b/psutil/_common.py
index cc223cf8..4298c7ee 100644
--- a/psutil/_common.py
+++ b/psutil/_common.py
@@ -56,6 +56,18 @@ CONN_LISTEN = "LISTEN"
CONN_CLOSING = "CLOSING"
CONN_NONE = "NONE"
+if enum is None:
+ NIC_DUPLEX_FULL = 2
+ NIC_DUPLEX_HALF = 1
+ NIC_DUPLEX_UNKNOWN = 0
+else:
+ class NicDuplex(enum.IntEnum):
+ NIC_DUPLEX_FULL = 2
+ NIC_DUPLEX_HALF = 1
+ NIC_DUPLEX_UNKNOWN = 0
+
+ globals().update(NicDuplex.__members__)
+
# --- functions
@@ -240,6 +252,8 @@ sconn = namedtuple('sconn', ['fd', 'family', 'type', 'laddr', 'raddr',
'status', 'pid'])
# psutil.net_if_addrs()
snic = namedtuple('snic', ['family', 'address', 'netmask', 'broadcast'])
+# psutil.net_if_stats
+snicstats = namedtuple('snicstats', ['isup', 'duplex', 'speed', 'mtu'])
# --- namedtuples for psutil.Process methods
diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py
index de77e859..960ec598 100644
--- a/psutil/_pslinux.py
+++ b/psutil/_pslinux.py
@@ -24,6 +24,7 @@ from . import _psposix
from . import _psutil_linux as cext
from . import _psutil_posix as cext_posix
from ._common import isfile_strict, usage_percent, deprecated
+from ._common import NIC_DUPLEX_FULL, NIC_DUPLEX_HALF, NIC_DUPLEX_UNKNOWN
from ._compat import PY3
if sys.version_info >= (3, 4):
@@ -570,6 +571,20 @@ def net_io_counters():
return retdict
+def net_if_stats():
+ """Get NIC stats (isup, duplex, speed, mtu)."""
+ duplex_map = {cext.DUPLEX_FULL: NIC_DUPLEX_FULL,
+ cext.DUPLEX_HALF: NIC_DUPLEX_HALF,
+ cext.DUPLEX_UNKNOWN: NIC_DUPLEX_UNKNOWN}
+ names = net_io_counters().keys()
+ ret = {}
+ for name in names:
+ isup, duplex, speed, mtu = cext.net_if_stats(name)
+ duplex = duplex_map[duplex]
+ ret[name] = _common.snicstats(isup, duplex, speed, mtu)
+ return ret
+
+
net_if_addrs = cext_posix.net_if_addrs
diff --git a/psutil/_psutil_linux.c b/psutil/_psutil_linux.c
index 921e03fe..8ea0e303 100644
--- a/psutil/_psutil_linux.c
+++ b/psutil/_psutil_linux.c
@@ -19,6 +19,11 @@
#include <linux/version.h>
#include <sys/syscall.h>
#include <sys/sysinfo.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <linux/sockios.h>
+#include <linux/if.h>
+#include <linux/ethtool.h>
#include "_psutil_linux.h"
@@ -476,6 +481,92 @@ error:
/*
+ * Return stats (isup?, duplex, speed) about a particular network
+ * interface. References:
+ * https://github.com/dpaleino/wicd/blob/master/wicd/backends/be-ioctl.py
+ * http://www.i-scream.org/libstatgrab/
+ */
+static PyObject*
+psutil_net_if_stats(PyObject* self, PyObject* args)
+{
+ char *nic_name;
+ int sock = 0;
+ int ret;
+ int duplex;
+ int speed;
+ struct ifreq ifr;
+ struct ethtool_cmd ethcmd;
+ PyObject *py_is_up = NULL;
+ PyObject *py_mtu = NULL;
+ PyObject *py_ret = NULL;
+
+ if (! PyArg_ParseTuple(args, "s", &nic_name))
+ return NULL;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock == -1)
+ goto error;
+ strncpy(ifr.ifr_name, nic_name, sizeof ifr.ifr_name);
+
+ // is up?
+ ret = ioctl(sock, SIOCGIFFLAGS, &ifr);
+ if (ret == -1)
+ goto error;
+ if ((ifr.ifr_flags & IFF_UP) != 0)
+ py_is_up = Py_True;
+ else
+ py_is_up = Py_False;
+ Py_INCREF(py_is_up);
+
+ // MTU
+ ret = ioctl(sock, SIOCGIFMTU, &ifr);
+ if (ret == -1)
+ goto error;
+ py_mtu = Py_BuildValue("i", ifr.ifr_mtu);
+ if (!py_mtu)
+ goto error;
+ Py_INCREF(py_mtu);
+
+ // duplex and speed
+ memset(&ethcmd, 0, sizeof ethcmd);
+ ethcmd.cmd = ETHTOOL_GSET;
+ ifr.ifr_data = (caddr_t)&ethcmd;
+ ret = ioctl(sock, SIOCETHTOOL, &ifr);
+
+ if (ret != -1) {
+ duplex = ethcmd.duplex;
+ speed = ethcmd.speed;
+ }
+ else {
+ if (errno == EOPNOTSUPP) {
+ // we typically get here in case of wi-fi cards
+ duplex = DUPLEX_UNKNOWN;
+ speed = 0;
+ }
+ else {
+ goto error;
+ }
+ }
+
+ close(sock);
+ py_ret = Py_BuildValue("[OiiO]", py_is_up, duplex, speed, py_mtu);
+ if (!py_ret)
+ goto error;
+ Py_DECREF(py_is_up);
+ Py_DECREF(py_mtu);
+ return py_ret;
+
+error:
+ Py_XDECREF(py_is_up);
+ Py_XDECREF(py_mtu);
+ if (sock != 0)
+ close(sock);
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+}
+
+
+/*
* Define the psutil C module methods and initialize the module.
*/
static PyMethodDef
@@ -501,6 +592,8 @@ PsutilMethods[] =
"device, mount point and filesystem type"},
{"users", psutil_users, METH_VARARGS,
"Return currently connected users as a list of tuples"},
+ {"net_if_stats", psutil_net_if_stats, METH_VARARGS,
+ "Return NIC stats (isup, duplex, speed, mtu)"},
// --- linux specific
@@ -599,6 +692,9 @@ void init_psutil_linux(void)
PyModule_AddIntConstant(module, "RLIMIT_SIGPENDING", RLIMIT_SIGPENDING);
#endif
#endif
+ PyModule_AddIntConstant(module, "DUPLEX_HALF", DUPLEX_HALF);
+ PyModule_AddIntConstant(module, "DUPLEX_FULL", DUPLEX_FULL);
+ PyModule_AddIntConstant(module, "DUPLEX_UNKNOWN", DUPLEX_UNKNOWN);
if (module == NULL) {
INITERROR;
diff --git a/psutil/_psutil_linux.h b/psutil/_psutil_linux.h
index 04ffec3d..ec6a3387 100644
--- a/psutil/_psutil_linux.h
+++ b/psutil/_psutil_linux.h
@@ -18,3 +18,4 @@ static PyObject* psutil_proc_ioprio_get(PyObject* self, PyObject* args);
static PyObject* psutil_disk_partitions(PyObject* self, PyObject* args);
static PyObject* psutil_linux_sysinfo(PyObject* self, PyObject* args);
static PyObject* psutil_users(PyObject* self, PyObject* args);
+static PyObject* psutil_net_if_stats(PyObject* self, PyObject* args);
diff --git a/test/test_memory_leaks.py b/test/test_memory_leaks.py
index 1441daa1..acfc3864 100644
--- a/test/test_memory_leaks.py
+++ b/test/test_memory_leaks.py
@@ -412,6 +412,9 @@ class TestModuleFunctionsLeaks(Base):
def test_net_if_addrs(self):
self.execute('net_if_addrs')
+ def test_net_if_stats(self):
+ self.execute('net_if_stats')
+
def test_main():
test_suite = unittest.TestSuite()
diff --git a/test/test_psutil.py b/test/test_psutil.py
index 979c77a5..dad4ddc7 100644
--- a/test/test_psutil.py
+++ b/test/test_psutil.py
@@ -1090,6 +1090,27 @@ class TestSystemAPIs(unittest.TestCase):
elif WINDOWS:
self.assertEqual(psutil.AF_LINK, -1)
+ def test_net_if_stats(self):
+ nics = psutil.net_if_stats()
+ assert nics, nics
+ all_duplexes = (psutil.NIC_DUPLEX_FULL,
+ psutil.NIC_DUPLEX_HALF,
+ psutil.NIC_DUPLEX_UNKNOWN)
+ for nic, stats in nics.items():
+ isup, duplex, speed, mtu = stats
+ self.assertIsInstance(isup, bool)
+ self.assertIn(duplex, all_duplexes)
+ self.assertIn(duplex, all_duplexes)
+ self.assertGreaterEqual(speed, 0)
+ self.assertGreaterEqual(mtu, 0)
+
+ def test_net_functions_names(self):
+ a = psutil.net_io_counters(pernic=True).keys()
+ b = psutil.net_if_addrs().keys()
+ c = psutil.net_if_stats().keys()
+ self.assertEqual(sorted(a), sorted(b))
+ self.assertEqual(sorted(b), sorted(c))
+
@unittest.skipIf(LINUX and not os.path.exists('/proc/diskstats'),
'/proc/diskstats not available on this linux version')
def test_disk_io_counters(self):