summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Galić <me+git@igalic.co>2020-01-10 05:06:06 +0100
committerChad Smith <chad.smith@canonical.com>2020-01-09 21:06:06 -0700
commitc911afbfe5a38823971e9cdbd4d1848c6e5c16de (patch)
tree4c6f04f883e0394b6bfc1d24d94c7db294e76fdd
parent6e7f8590402967bffbdd0bb0ae241180910fcd2c (diff)
downloadvyos-cloud-init-c911afbfe5a38823971e9cdbd4d1848c6e5c16de.tar.gz
vyos-cloud-init-c911afbfe5a38823971e9cdbd4d1848c6e5c16de.zip
util: move uptime's else branch into its own boottime function (#53)
Also fix bugs: - pass binary instead of string to sysctlbyname(), and - unpack the "return value" in a struct, rather than in single integer. LP: #1853160 Co-Authored-By: Ryan Harper <ryan.harper@canonical.com>
-rw-r--r--cloudinit/tests/test_util.py15
-rw-r--r--cloudinit/util.py39
2 files changed, 44 insertions, 10 deletions
diff --git a/cloudinit/tests/test_util.py b/cloudinit/tests/test_util.py
index be100646..11f37000 100644
--- a/cloudinit/tests/test_util.py
+++ b/cloudinit/tests/test_util.py
@@ -189,6 +189,21 @@ class TestUtil(CiTestCase):
self.assertEqual(is_rw, False)
+class TestUptime(CiTestCase):
+
+ @mock.patch('cloudinit.util.boottime')
+ @mock.patch('cloudinit.util.os.path.exists')
+ @mock.patch('cloudinit.util.time.time')
+ def test_uptime_non_linux_path(self, m_time, m_exists, m_boottime):
+ boottime = 1000.0
+ uptime = 10.0
+ m_boottime.return_value = boottime
+ m_time.return_value = boottime + uptime
+ m_exists.return_value = False
+ result = util.uptime()
+ self.assertEqual(str(uptime), result)
+
+
class TestShellify(CiTestCase):
def test_input_dict_raises_type_error(self):
diff --git a/cloudinit/util.py b/cloudinit/util.py
index 830c8e54..76d7db78 100644
--- a/cloudinit/util.py
+++ b/cloudinit/util.py
@@ -10,7 +10,6 @@
import contextlib
import copy as obj_copy
-import ctypes
import email
import glob
import grp
@@ -1807,6 +1806,33 @@ def time_rfc2822():
return ts
+def boottime():
+ """Use sysctlbyname(3) via ctypes to find kern.boottime
+
+ kern.boottime is of type struct timeval. Here we create a
+ private class to easier unpack it.
+
+ @return boottime: float to be compatible with linux
+ """
+ import ctypes
+
+ NULL_BYTES = b"\x00"
+
+ class timeval(ctypes.Structure):
+ _fields_ = [
+ ("tv_sec", ctypes.c_int64),
+ ("tv_usec", ctypes.c_int64)
+ ]
+ libc = ctypes.CDLL('/lib/libc.so.7')
+ size = ctypes.c_size_t()
+ size.value = ctypes.sizeof(timeval)
+ buf = timeval()
+ if libc.sysctlbyname(b"kern.boottime" + NULL_BYTES, ctypes.byref(buf),
+ ctypes.byref(size), None, 0) != -1:
+ return buf.tv_sec + buf.tv_usec / 1000000.0
+ raise RuntimeError("Unable to retrieve kern.boottime on this system")
+
+
def uptime():
uptime_str = '??'
method = 'unknown'
@@ -1818,15 +1844,8 @@ def uptime():
uptime_str = contents.split()[0]
else:
method = 'ctypes'
- libc = ctypes.CDLL('/lib/libc.so.7')
- size = ctypes.c_size_t()
- buf = ctypes.c_int()
- size.value = ctypes.sizeof(buf)
- libc.sysctlbyname("kern.boottime", ctypes.byref(buf),
- ctypes.byref(size), None, 0)
- now = time.time()
- bootup = buf.value
- uptime_str = now - bootup
+ # This is the *BSD codepath
+ uptime_str = str(time.time() - boottime())
except Exception:
logexc(LOG, "Unable to read uptime using method: %s" % method)