You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
387 lines
14 KiB
387 lines
14 KiB
#!/usr/bin/env python
|
|
|
|
# @HEADER
|
|
# ************************************************************************
|
|
#
|
|
# TriBITS: Tribal Build, Integrate, and Test System
|
|
# Copyright 2013 Sandia Corporation
|
|
#
|
|
# Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
|
|
# the U.S. Government retains certain rights in this software.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions are
|
|
# met:
|
|
#
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
#
|
|
# 2. Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
# documentation and/or other materials provided with the distribution.
|
|
#
|
|
# 3. Neither the name of the Corporation nor the names of the
|
|
# contributors may be used to endorse or promote products derived from
|
|
# this software without specific prior written permission.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
|
|
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
|
|
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
#
|
|
# ************************************************************************
|
|
# @HEADER
|
|
|
|
import datetime
|
|
|
|
#
|
|
# Help message
|
|
#
|
|
|
|
|
|
usageHelp = r"""cdash_testing_date.py --cdash-project-start-time="hh:mm" [other options]
|
|
|
|
Returns to STDOUT the date string YYYY-MM-DD that corresponds the the matching
|
|
CDash test day. Is should be compatible with the 'date=YYYY-MM-DD' argument
|
|
to many CDash PHP pages for a build that has a starting time stamp that
|
|
matches the requested date.
|
|
|
|
This tool is meant to match the CDash logic for what testing day a given CDash
|
|
build matches for the CDash 'date' field in various PHP pages for a given
|
|
CDash build with a give build start time (as reported to CDash from CTest).
|
|
|
|
For example, if the CDash project start time is 04:00 UTC and the CDash
|
|
build start time is 2018-01-22T00:00 UTC, then the relative CDash build date
|
|
time would be 2018-01-21T20:00 UTC which is the previous testing day
|
|
2018-01-21. But if the CDash build start time was 2018-01-22T04:10 UTC,
|
|
then the relative CDash build date time would be 2018-01-22:00:10 UTC and
|
|
the testing day would be 2018-01-22.
|
|
"""
|
|
|
|
#
|
|
# Helper functions
|
|
#
|
|
|
|
|
|
def injectCmndLineOptionsInParser(clp, gitoliteRootDefault=""):
|
|
|
|
clp.add_option(
|
|
"--cdash-project-start-time", dest="cdashProjectStartTimeUtcStr", type="string", default="",
|
|
help="Starting time for the CDash testing day in 'hh:mm' in UTC."\
|
|
+ " Check the CDash project settings for the testing day start time." )
|
|
|
|
clp.add_option(
|
|
"--day-incr", dest="dayIncrInt", type="int", default="0",
|
|
help="Increment for the testing date (default '0') [optional]" )
|
|
|
|
clp.add_option(
|
|
"--cdash-build-start-time", dest="cdashBuildStartTime", type="string", default="",
|
|
help="CDash build start time in format 'YYYY-MM-DDThh:mm UTC'. If empty ''"\
|
|
+" then the current date/time in UTC is used. (default '') [optional]" )
|
|
|
|
clp.add_option(
|
|
"--debug-level", dest="debugLevel", type="int", default="0",
|
|
help="Debug level. An integer >= 0 (default '0')" )
|
|
|
|
|
|
def getCmndLineOptions():
|
|
from optparse import OptionParser
|
|
clp = OptionParser(usage=usageHelp)
|
|
injectCmndLineOptionsInParser(clp)
|
|
(options, args) = clp.parse_args()
|
|
if options.cdashProjectStartTimeUtcStr == "":
|
|
raise Exception("Error, input argument --cdash-project-start-time must be non"\
|
|
+"-empty and must be of the format hh:mm UTC")
|
|
return options
|
|
|
|
|
|
# Return the current time in UTC as a datetime object
|
|
def getCurrentDateTimeUtc():
|
|
return datetime.datetime.utcnow()
|
|
|
|
|
|
# Get the timezone offset as a timedelta object w.r.t to UTC
|
|
#
|
|
# The supported timezones for timeZoneStr are the strings:
|
|
#
|
|
# * UTC: 0
|
|
# * EDT: 4
|
|
# * EST: 5
|
|
# * CDT: 5
|
|
# * CST: 6
|
|
# * MDT: 6
|
|
# * MST: 7
|
|
#
|
|
# NOTE: Any timezone that CDash returns for the 'buildstarttime' field must be
|
|
# added below.
|
|
#
|
|
def getTimeZoneOffset(timeZoneStr):
|
|
if timeZoneStr == "UTC": timezoneOffsetInt = 0
|
|
elif timeZoneStr == "EDT": timezoneOffsetInt = 4
|
|
elif timeZoneStr == "EST": timezoneOffsetInt = 5
|
|
elif timeZoneStr == "CDT": timezoneOffsetInt = 5
|
|
elif timeZoneStr == "CST": timezoneOffsetInt = 6
|
|
elif timeZoneStr == "MDT": timezoneOffsetInt = 6
|
|
elif timeZoneStr == "MST": timezoneOffsetInt = 7
|
|
else: raise Exception("Error, unrecognized timezone '"+timeZoneStr+"'!")
|
|
return datetime.timedelta(hours=timezoneOffsetInt)
|
|
|
|
|
|
# Return a timezone aware datetime object given an input date and time given
|
|
# in the format "<YYYY>-<MM>-<DD>T<hh>:<mm>:<ss> <TZ>".
|
|
#
|
|
# Note. the timzone <TZ> can be any of those supported by the function
|
|
# getTimeZoneOffset()
|
|
def getBuildStartTimeUtcFromStr(buildStartTimeStr):
|
|
buildStartTimeStrArray = buildStartTimeStr.split(" ")
|
|
if len(buildStartTimeStrArray) == 2:
|
|
timezoneOffset = getTimeZoneOffset(buildStartTimeStrArray[1])
|
|
else:
|
|
timezoneOffset = 0
|
|
localDateTime = datetime.datetime.strptime(buildStartTimeStrArray[0], "%Y-%m-%dT%H:%M:%S")
|
|
return localDateTime + timezoneOffset
|
|
|
|
|
|
# Return a timedelta object for the CDash Project start time passed in as a
|
|
# string in the format "<hh>:<mm>" in UTC.
|
|
def getProjectTestingDayStartTimeDeltaFromStr(cdashProjectStartTimeUtcStr):
|
|
t = datetime.datetime.strptime(cdashProjectStartTimeUtcStr, '%H:%M')
|
|
return datetime.timedelta(hours=t.hour, minutes=t.minute, seconds=t.second)
|
|
|
|
|
|
# Return a timedelta object for a day increment pass in as an signed integer.
|
|
def getDayIncrTimeDeltaFromInt(dayIncrInt):
|
|
return datetime.timedelta(days=dayIncrInt)
|
|
|
|
|
|
# Return the string "YYYY-MM-DDThh:mm:ss UTC" corresponding to the input
|
|
# datetime object.
|
|
def getBuildStartTimeUtcStrFromUtcDT(datetimeUtcDT, removeSpace=False):
|
|
dateTimeStr = datetimeUtcDT.strftime("%Y-%m-%dT%H:%M:%S UTC")
|
|
if removeSpace:
|
|
return dateTimeStr.replace(" ", "")
|
|
return dateTimeStr
|
|
|
|
|
|
# Return the string "<YYYY>-<MM>-<DD>" for an input datetime object.
|
|
def getDateStrFromDateTime(dateTime):
|
|
return dateTime.strftime("%Y-%m-%d")
|
|
|
|
|
|
# Return the datetime object for just <YYYY>-<MM>-<DD> for an input datetime
|
|
# object.
|
|
def getDateOnlyFromDateTime(dateTime):
|
|
return datetime.datetime(year=dateTime.year, month=dateTime.month, day=dateTime.day)
|
|
|
|
|
|
# Compute the CDash testing day for a given build using its 'buildstarttime'
|
|
# field and the testing day start time and return it as a datetime object.
|
|
#
|
|
# buildStartTimeStr [in]: The 'buildstarttime' field string as returned from
|
|
# CDash in the format "<YYYY>-<MM>-<DD>T<hh>:<mm>:<ss> <TZ>". Note. the
|
|
# timzone <TZ> can be any of those supported by the function
|
|
# getTimeZoneOffset()
|
|
#
|
|
# testingDayStartTimeUtcTD [in]: The testing day start time as the a timedelta
|
|
# object.
|
|
#
|
|
# ToDo: Take into account the CDash server timezone which determine what noon
|
|
# means!
|
|
#
|
|
def getTestingDayDateFromBuildStartTimeDT(
|
|
buildStartTimeStr, testingDayStartTimeUtcTD,
|
|
):
|
|
|
|
#print()
|
|
#print("testingDayStartTimeUtcTD = "+str(testingDayStartTimeUtcTD))
|
|
#print("testingDayStartTimeUtcTD.seconds = "+str(testingDayStartTimeUtcTD.seconds))
|
|
|
|
# Constants
|
|
oneDayTD = datetime.timedelta(days=1)
|
|
noonTD = getProjectTestingDayStartTimeDeltaFromStr("12:00")
|
|
|
|
# Convert input 'buildstartime' to datetime object in UtC
|
|
buildStartTimeUtcDT = getBuildStartTimeUtcFromStr(buildStartTimeStr)
|
|
|
|
# Get the date and time separately for the input 'buildstartime'
|
|
buildStartTimeDateDT = datetime.datetime(
|
|
year=buildStartTimeUtcDT.year,
|
|
month=buildStartTimeUtcDT.month,
|
|
day=buildStartTimeUtcDT.day,
|
|
)
|
|
#print("buildStartTimeDateDT = "+str(buildStartTimeDateDT))
|
|
buildStartTimeTimeTD = buildStartTimeUtcDT - buildStartTimeDateDT
|
|
#print("buildStartTimeTimeTD = "+str(buildStartTimeTimeTD))
|
|
#print("buildStartTimeTimeTD.seconds = "+str(buildStartTimeTimeTD.seconds))
|
|
|
|
if buildStartTimeTimeTD.seconds < testingDayStartTimeUtcTD.seconds:
|
|
buildStartTimeDateDT -= oneDayTD
|
|
#print("buildStartTimeDateDT = "+str(buildStartTimeDateDT))
|
|
|
|
if testingDayStartTimeUtcTD > noonTD:
|
|
buildStartTimeDateDT += oneDayTD
|
|
#print("buildStartTimeDateDT = "+str(buildStartTimeDateDT))
|
|
|
|
return buildStartTimeDateDT
|
|
|
|
|
|
# Compute the CDash testing day for a given build using its 'buildstarttime'
|
|
# field and the testing day start time and return as a string "YYYY-MM-DD".
|
|
#
|
|
# See getTestingDayDateFromBuildStartTimeDT()
|
|
#
|
|
def getTestingDayDateFromBuildStartTimeStr(
|
|
buildStartTimeStr, testingDayStartTimeUtcTD,
|
|
):
|
|
return getDateStrFromDateTime(
|
|
getTestingDayDateFromBuildStartTimeDT(buildStartTimeStr, testingDayStartTimeUtcTD) )
|
|
|
|
|
|
# Return the shifted CDash build start relative to the given CDash project
|
|
# testing day start time (as configured on CDash).
|
|
#
|
|
# This function is meant to match the CDash logic for what testing day a given
|
|
# CDash build matches for the CDash 'date' field in various PHP pages for a
|
|
# given CDash build with a give build start time (as reported to CDash from
|
|
# CTest).
|
|
#
|
|
# For example, if the CDash project start time is 04:00 UTC and the CDash
|
|
# build start time is 2018-01-22T00:00 UTC, then the relative CDash build date
|
|
# time would be 2018-01-21T20:00 UTC which is the previous testing day
|
|
# 2018-01-21. But if the CDash build start time was 2018-01-22T04:10 UTC,
|
|
# then the relative CDash build date time would be 2018-01-22:00:10 UTC and
|
|
# the testing day would be 2018-01-22.
|
|
#
|
|
# To extract just the year, month and day from the returned datetime object as
|
|
# a datetime object, use the function getDateOnlyFromDateTime().
|
|
#
|
|
# To extract the "<YYYY>-<MM>-<DD>" string from the returned datetime object,
|
|
# use the function getDateStrFromDateTime().
|
|
#
|
|
def getRelativeCDashBuildStartTime(
|
|
cdashBuildStartTimeUtc, # CDash build start time in UTC (datetime object)
|
|
cdashProjectStartTimeUtc, # CDash start time in UTC (timedelta object)
|
|
dayIncr, # Day increment in past or future (timedelta object)
|
|
):
|
|
relativeCDashBuildDateTime = \
|
|
cdashBuildStartTimeUtc - cdashProjectStartTimeUtc + dayIncr
|
|
return relativeCDashBuildDateTime
|
|
|
|
|
|
# Return the shifted CDash build start relative to the given CDash project
|
|
# testing day start time given input from the command-line arguments.
|
|
#
|
|
# This function just converts the input command-line args and then calls
|
|
# getRelativeCDashBuildStartTime().
|
|
#
|
|
# Arguments:
|
|
#
|
|
# cdashBuildStartTimeStr [in] Reference build start time in
|
|
# "YYY-MM-DDThh:mm:ss TZ". If 'None', then is taken from
|
|
# getCurrentDateTimeUtc().
|
|
#
|
|
# cdashProjectStartTimeUtcStr [in] CDash project build start time in
|
|
# UTC in the format "hh:mm".
|
|
#
|
|
# dayIncrInt [in] Integer for the day to return relative to
|
|
# cdashProjectStartTimeUtcStr. The current testing day would be 0. Yesterday
|
|
# would be -1 and so on.
|
|
#
|
|
# debugLevel [in] If 0, then no debugging. If > 0, then debug output will be
|
|
# printed.
|
|
#
|
|
def getRelativeCDashBuildStartTimeFromCmndLineArgs(
|
|
cdashBuildStartTimeStr,
|
|
cdashProjectStartTimeUtcStr,
|
|
dayIncrInt,
|
|
debugLevel=0,
|
|
):
|
|
|
|
if cdashBuildStartTimeStr:
|
|
buildStartTimeUtc = getBuildStartTimeUtcFromStr(cdashBuildStartTimeStr)
|
|
else:
|
|
buildStartTimeUtc = getCurrentDateTimeUtc()
|
|
if debugLevel: print("buildStartTimeUtc = "+str(buildStartTimeUtc))
|
|
|
|
cdashStartTime = \
|
|
getProjectTestingDayStartTimeDeltaFromStr(cdashProjectStartTimeUtcStr)
|
|
if debugLevel: print("cdashStartTime = "+str(cdashStartTime))
|
|
|
|
dayIncr = getDayIncrTimeDeltaFromInt(dayIncrInt)
|
|
if debugLevel: print("dayIncr = "+str(dayIncr))
|
|
|
|
return getRelativeCDashBuildStartTime(buildStartTimeUtc, cdashStartTime, dayIncr)
|
|
|
|
|
|
# Class to encapsulate handling of CDash testing day logic
|
|
#
|
|
class CDashProjectTestingDay(object):
|
|
|
|
# Construct
|
|
#
|
|
# currentTestingDayDateStr [in]: The current project CDash testing date in
|
|
# string format "YYYY-MM-DD".
|
|
#
|
|
# projectTestingDayStartTimeUtcStr [in]: The CDash projects's testing day start
|
|
# time in UTC. This is a string in the format "hh:mm".
|
|
#
|
|
def __init__(self, currentTestingDayDateStr, projectTestingDayStartTimeUtcStr):
|
|
# Store input args
|
|
self.__currentTestingDayDateStr = currentTestingDayDateStr
|
|
self.__projectTestingDayStartTimeUtcStr = projectTestingDayStartTimeUtcStr
|
|
# Set the start of the testing day in UTC
|
|
self.__currentTestingDayDateDT = \
|
|
datetime.datetime.strptime(currentTestingDayDateStr, "%Y-%m-%d")
|
|
self.__projectTestingDayStartTimeUtcTD = \
|
|
getProjectTestingDayStartTimeDeltaFromStr(projectTestingDayStartTimeUtcStr)
|
|
noonTD = getProjectTestingDayStartTimeDeltaFromStr("12:00")
|
|
oneDayTD = datetime.timedelta(days=1)
|
|
if self.__projectTestingDayStartTimeUtcTD >= noonTD:
|
|
self.__testingDayStartDateTimeUtcDT = \
|
|
self.__currentTestingDayDateDT - oneDayTD + self.__projectTestingDayStartTimeUtcTD
|
|
else:
|
|
self.__testingDayStartDateTimeUtcDT = \
|
|
self.__currentTestingDayDateDT + self.__projectTestingDayStartTimeUtcTD
|
|
|
|
# Return the testing day start in UTC as a datetime object
|
|
def getTestingDayStartUtcDT(self):
|
|
return self.__testingDayStartDateTimeUtcDT
|
|
|
|
# Return the testing day date (not time) as a datetime object
|
|
def getCurrentTestingDayDateDT(self):
|
|
return self.__currentTestingDayDateDT
|
|
|
|
# Return the testing day as a datetime object for the input 'buildstartime'
|
|
# string.
|
|
def getTestingDayDateFromBuildStartTimeDT(self, buildStartTimeStr):
|
|
return getTestingDayDateFromBuildStartTimeDT(
|
|
buildStartTimeStr, self.__projectTestingDayStartTimeUtcTD)
|
|
|
|
# Return the testing day string "YYYY-MM-DD" for the input 'buildstartime'
|
|
# string.
|
|
def getTestingDayDateFromBuildStartTimeStr(self, buildStartTimeStr):
|
|
return getDateStrFromDateTime(
|
|
self.getTestingDayDateFromBuildStartTimeDT(buildStartTimeStr) )
|
|
|
|
|
|
#
|
|
# Run the script
|
|
#
|
|
|
|
if __name__ == '__main__':
|
|
|
|
inOptions = getCmndLineOptions()
|
|
|
|
relativeCDashBuildBuildDateTime = getRelativeCDashBuildStartTimeFromCmndLineArgs(
|
|
inOptions.cdashBuildStartTime, inOptions.cdashProjectStartTimeUtcStr,
|
|
inOptions.dayIncrInt, inOptions.debugLevel,
|
|
)
|
|
|
|
print(getDateStrFromDateTime(relativeCDashBuildBuildDateTime))
|
|
|