""" The Machine/Job Features TimeLeft utility interrogates the MJF values for the current CPU and Wallclock consumed, as well as their limits. """ from __future__ import absolute_import from __future__ import division from __future__ import print_function __RCSID__ = "c59810311 (2021-10-01 10:44:25 +0200) Chris Burr " import os import time from six.moves.urllib.request import urlopen from DIRAC import S_OK, S_ERROR from DIRAC.Resources.Computing.BatchSystems.TimeLeft.ResourceUsage import ResourceUsage class MJFResourceUsage(ResourceUsage): """ This is the MJF plugin of the TimeLeft Utility """ ############################################################################# def __init__(self): """Standard constructor""" super(MJFResourceUsage, self).__init__("MJF", "JOB_ID") self.queue = os.environ.get("QUEUE") self.log.verbose("jobID=%s, queue=%s" % (self.jobID, self.queue)) self.startTime = time.time() ############################################################################# def getResourceUsage(self): """Returns S_OK with a dictionary containing the entries CPU, CPULimit, WallClock, WallClockLimit, and Unit for current slot. """ cpuLimit = None wallClockLimit = None wallClock = None jobStartSecs = None jobFeaturesPath = None machineFeaturesPath = None # Getting info from JOBFEATURES try: # We are not called from TimeLeft.py if these are not set jobFeaturesPath = os.environ["JOBFEATURES"] except KeyError: self.log.warn("$JOBFEATURES is not set") if jobFeaturesPath: try: wallClockLimit = int(urlopen(jobFeaturesPath + "/wall_limit_secs").read()) self.log.verbose("wallClockLimit from JF = %d" % wallClockLimit) except ValueError: self.log.warn("/wall_limit_secs is unreadable") except IOError as e: self.log.exception("Issue with $JOBFEATURES/wall_limit_secs", lException=e) self.log.warn("Could not determine cpu limit from $JOBFEATURES/wall_limit_secs") try: jobStartSecs = int(urlopen(jobFeaturesPath + "/jobstart_secs").read()) self.log.verbose("jobStartSecs from JF = %d" % jobStartSecs) except ValueError: self.log.warn("/jobstart_secs is unreadable, setting a default") jobStartSecs = self.startTime except IOError as e: self.log.exception("Issue with $JOBFEATURES/jobstart_secs", lException=e) self.log.warn("Can't open jobstart_secs, setting a default") jobStartSecs = self.startTime try: cpuLimit = int(urlopen(jobFeaturesPath + "/cpu_limit_secs").read()) self.log.verbose("cpuLimit from JF = %d" % cpuLimit) except ValueError: self.log.warn("/cpu_limit_secs is unreadable") except IOError as e: self.log.exception("Issue with $JOBFEATURES/cpu_limit_secs", lException=e) self.log.warn("Could not determine cpu limit from $JOBFEATURES/cpu_limit_secs") wallClock = int(time.time()) - jobStartSecs # Getting info from MACHINEFEATURES try: # We are not called from TimeLeft.py if these are not set machineFeaturesPath = os.environ["MACHINEFEATURES"] except KeyError: self.log.warn("$MACHINEFEATURES is not set") if machineFeaturesPath and jobStartSecs: try: shutdownTime = int(urlopen(machineFeaturesPath + "/shutdowntime").read()) self.log.verbose("shutdownTime from MF = %d" % shutdownTime) if int(time.time()) + wallClockLimit > shutdownTime: # reduce wallClockLimit if would overrun shutdownTime wallClockLimit = shutdownTime - jobStartSecs except ValueError: self.log.warn("/shutdowntime is unreadable") except IOError as e: self.log.warn("Issue with $MACHINEFEATURES/shutdowntime", repr(e)) self.log.warn("Could not determine a shutdowntime value from $MACHINEFEATURES/shutdowntime") # Reporting consumed = {"CPU": None, "CPULimit": cpuLimit, "WallClock": wallClock, "WallClockLimit": wallClockLimit} if cpuLimit and wallClock and wallClockLimit: self.log.verbose("MJF consumed: %s" % str(consumed)) return S_OK(consumed) self.log.info("Could not determine some parameters") retVal = S_ERROR("Could not determine some parameters") retVal["Value"] = consumed return retVal