commit 004d8a3e46fe4c56b0ea4db27c2c618b86b54f76 Author: Nathan Scott 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 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