commit 004d8a3e46fe4c56b0ea4db27c2c618b86b54f76
Author: Nathan Scott <nathans@redhat.com>
Date: Mon Feb 3 17:13:12 2014 +1100
Fix and test python wrappers for pmLocaltime and pmCtime
Several problems uncovered in the time-processing wrappers
of the python PMAPI module after attempting to use 'em for
the first time:
- size of 'struct tm' did not match modern reality (missing
the final two fields for Linux and BSD - sigsegv results);
- system time.mktime python method does not take 9 arguments
just one - fix the string-printing method for struct tm;
- system time.mktime takes year field as an actual year, and
not the number of years since 1900 - code didn't cater for
this difference to struct tm form returned from localtime.
- definition of the pmLocaltime return code was not correct;
- definition of first pmCtime parameter was not correct;
- code didn't cope with numeric input being passed in either
float/integer format - pythonic time-in-seconds is float.
- no memory allocated to hold the struct tm that pmLocaltime
returns - pointer passed in pointing off into lala-land;
- need to ensure a pointer to a ctypes c_long is passed into
both of these routines - caller might well pass int (e.g.
from a pmResult timestamp (struct timeval, tv_sec field).
Fixed the above and added qa test (boeing) 737 to verify it.
Some small cleanups as well, in the affected code - fixed a
couple of typos in comments, and removed an unused import
of struct_time from the system time module.
diff --git a/qa/737 b/qa/737
new file mode 100755
index 0000000..17d6c18
--- /dev/null
+++ b/qa/737
@@ -0,0 +1,29 @@
+#!/bin/sh
+# PCP QA Test No. 737
+# Exercise time interfaces in python PMAPI wrapper module.
+#
+# Copyright (c) Red Hat.
+#
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+# get standard environment, filters and checks
+. ./common.product
+. ./common.filter
+. ./common.check
+
+python -c "from pcp import pmda" >/dev/null 2>&1
+[ $? -eq 0 ] || _notrun "python pcp pmda module not installed"
+
+status=1 # failure is the default!
+$sudo rm -rf $tmp.* $seq.full
+trap "rm -rf $tmp.*; exit \$status" 0 1 2 3 15
+
+# real QA test starts here
+python src/test_pcp_time.python
+
+# success, all done
+status=0
+
+exit
diff --git a/qa/737.out b/qa/737.out
new file mode 100644
index 0000000..6a12e50
--- /dev/null
+++ b/qa/737.out
@@ -0,0 +1,5 @@
+QA output created by 737
+pmLocaltime from int: 1388763979.0 (2014, 1, 3, 15, 46, 19, 1, 33, 0)
+pmLocaltime from float: 1388763979.0 (2014, 1, 3, 15, 46, 19, 1, 33, 0)
+pmCtime from int: Mon Feb 3 15:46:19 2014
+pmCtime from float: Mon Feb 3 15:46:19 2014
diff --git a/qa/group b/qa/group
index 077b83c..bb752f1 100644
--- a/qa/group
+++ b/qa/group
@@ -883,6 +883,7 @@ avahi
733 pmie pmval pmdumplog local oss
735 pmda.proc local oss
736 pmlogrewrite local oss
+737 python local oss
740 pmda.sample pmstore secure local oss
748 pmlogrewrite pmda.mysql local oss
749 pmcd local oss
diff --git a/qa/src/GNUlocaldefs b/qa/src/GNUlocaldefs
index b4382ce..0cf7971 100644
--- a/qa/src/GNUlocaldefs
+++ b/qa/src/GNUlocaldefs
@@ -154,7 +154,8 @@ PLFILES = $(shell echo $(PERLFILES) | sed -e 's/\.perl/.pl/g')
PYTHONFILES = \
test_pcp.python test_pmcc.python \
test_pmi.python check_import.python \
- test_mmv.python test_webapi.python
+ test_mmv.python test_webapi.python \
+ test_pcp_time.python
# not installed:
PYFILES = $(shell echo $(PYTHONFILES) | sed -e 's/\.python/.py/g')
diff --git a/qa/src/test_pcp_time.python b/qa/src/test_pcp_time.python
new file mode 100755
index 0000000..674686b
--- /dev/null
+++ b/qa/src/test_pcp_time.python
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2014 Red Hat.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+
+""" Exercise the pmCtime and pmLocaltime interfaces """
+
+from pcp import pmapi
+
+""" Create a live PMAPI context, set as UTC and report time """
+context = pmapi.pmContext()
+context.pmNewZone("UTC")
+
+# seconds-since-epoch for: "Mon Feb 3 15:46:19 2014 UTC"
+sample_time_i = 1391402779 # integer
+sample_time_f = 1391402779.0 # float
+
+print "pmLocaltime from int: ", context.pmLocaltime(sample_time_i)
+print "pmLocaltime from float: ", context.pmLocaltime(sample_time_f)
+
+print "pmCtime from int: ", context.pmCtime(sample_time_i),
+print "pmCtime from float: ", context.pmCtime(sample_time_f),
diff --git a/src/python/pcp/pmapi.py b/src/python/pcp/pmapi.py
index 527a1f3..72302b4 100644
--- a/src/python/pcp/pmapi.py
+++ b/src/python/pcp/pmapi.py
@@ -56,7 +56,7 @@
# Is this the kernel.all.load id?
if (results.contents.get_pmid(i) != metric_ids[1]):
continue
- # Extrace the kernal.all.load instance
+ # Extract the kernel.all.load instance
for j in xrange(results.contents.get_numval(i) - 1):
atom = context.pmExtractValue(results.contents.get_valfmt(i),
results.contents.get_vlist(i, j),
@@ -69,8 +69,8 @@
print "load average 5=",atom.f
"""
-# for dereferencing times from pmLocaltime function (struct tm)
-from time import struct_time, mktime
+# for reporting on times from pmLocaltime function
+from time import mktime
# constants adapted from C header file <pcp/pmapi.h>
import cpmapi as c_api
@@ -148,13 +148,20 @@ class tm(Structure):
("tm_year", c_int),
("tm_wday", c_int),
("tm_yday", c_int),
- ("tm_isdst", c_int)]
+ ("tm_isdst", c_int),
+ ("tm_gmtoff", c_long), # glibc/bsd extension
+ ("tm_zone", c_char_p)] # glibc/bsd extension
def __str__(self):
- tmp = mktime(self.tm_year, self.tm_mon, self.tm_mday, self.tm_hour,
- self.tm_min, self.tm_sec, self.tm_wday, self.tm_yday,
- self.tm_isdst)
- return "%s" % tmp.__str__()
+ timetuple = (self.tm_year+1900, self.tm_mon, self.tm_mday,
+ self.tm_hour, self.tm_min, self.tm_sec,
+ self.tm_wday, self.tm_yday, self.tm_isdst)
+ inseconds = 0.0
+ try:
+ inseconds = mktime(timetuple)
+ except:
+ pass
+ return "%s %s" % (inseconds.__str__(), timetuple)
class pmAtomValue(Union):
"""Union used for unpacking metric values according to type
@@ -512,11 +519,11 @@ LIBPCP.pmUseZone.argtypes = [c_int]
LIBPCP.pmWhichZone.restype = c_int
LIBPCP.pmWhichZone.argtypes = [POINTER(c_char_p)]
-LIBPCP.pmLocaltime.restype = tm
+LIBPCP.pmLocaltime.restype = POINTER(tm)
LIBPCP.pmLocaltime.argtypes = [POINTER(c_long), POINTER(tm)]
LIBPCP.pmCtime.restype = c_char_p
-LIBPCP.pmCtime.argtypes = [c_long, c_char_p]
+LIBPCP.pmCtime.argtypes = [POINTER(c_long), c_char_p]
##
@@ -689,7 +696,7 @@ class pmContext(object):
status = LIBPCP.pmUseContext(self.ctx)
if status < 0:
raise pmErr, status
- status = LIBPCP.pmGetChildren(name, byref( offspring))
+ status = LIBPCP.pmGetChildren(name, byref(offspring))
if status < 0:
raise pmErr, status
if status > 0:
@@ -1148,16 +1155,20 @@ class pmContext(object):
status = LIBPCP.pmUseContext(self.ctx)
if status < 0:
raise pmErr, status
- result = POINTER(tm)()
- return LIBPCP.pmLocaltime(seconds, byref(result))
+ result = (tm)()
+ timetp = c_long(int(seconds))
+ LIBPCP.pmLocaltime(byref(timetp), byref(result))
+ return result
def pmCtime(self, seconds):
"""PMAPI - format the date and time for a reporting timezone """
status = LIBPCP.pmUseContext(self.ctx)
if status < 0:
raise pmErr, status
- result = (c_char * 32)()
- return LIBPCP.pmCtime(seconds, byref(result))
+ result = ctypes.create_string_buffer(32)
+ timetp = c_long(int(seconds))
+ LIBPCP.pmCtime(byref(timetp), result)
+ return str(result.value)
##
# PMAPI Metrics Services