Blob Blame History Raw
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