def HTCScheddFactory(URI, **kwargs): """ Return an interface for the GC-Schedd operations Required: URI string The URI of the Schedd to connect to """ adapter, scheme = ProcessAdapterFactory(URI, externalSchemes=["spool"]) if not adapter: raise NotImplementedError("Schedd interfacing via methods of scheme '%s' has not been implemented yet." % scheme) for HTCSchedd in [ HTCScheddLocal, HTCScheddSSH ]: if adapter.getType() in HTCSchedd.adapterTypes: return HTCSchedd(URI = URI, adapter = adapter, **kwargs)
def __init__(self, URI="", adapter = None, parentPool = None): """ Optional: URI string URI from which to construct an adapter if none given adapter ProcessAdapterInterface adapter to use for issuing schedd commands parentPool HTCondorWMS pool WMS the schedd belongs to """ self._initLogger() self._log(logging.INFO1, "Establishing HTC Schedd adapter of type %s" % self.__class__.__name__) if adapter: self._adapter = adapter else: self._adapter, _ = ProcessAdapterFactory(URI, externalSchemes=["spool"]) self._URI = URI or self._adapter.getURI() assert self._adapter != None, "Bug! Schedd initialization with invalid adapter data." assert adapter.getType() in self.adapterTypes, "Bug! Got adapter of type '%s', expected '%s'" % (adapter.getType(), "' or '".join(self.adapterType)) self.parentPool = parentPool
class HTCScheddBase(Plugin): """ Base Interface for interactions with a Schedd """ adapterTypes = [] _submitScale = 0 _adapterMaxWait = 10 def __init__(self, URI="", adapter = None, parentPool = None): """ Optional: URI string URI from which to construct an adapter if none given adapter ProcessAdapterInterface adapter to use for issuing schedd commands parentPool HTCondorWMS pool WMS the schedd belongs to """ self._initLogger() self._log(logging.INFO1, "Establishing HTC Schedd adapter of type %s" % self.__class__.__name__) if adapter: self._adapter = adapter else: self._adapter, _ = ProcessAdapterFactory(URI, externalSchemes=["spool"]) self._URI = URI or self._adapter.getURI() assert self._adapter != None, "Bug! Schedd initialization with invalid adapter data." assert adapter.getType() in self.adapterTypes, "Bug! Got adapter of type '%s', expected '%s'" % (adapter.getType(), "' or '".join(self.adapterType)) self.parentPool = parentPool def getDomain(self): return self._adapter.getDomain() def getURI(self): return self._URI # public interfaces for HTC Pool/WMS def submitJobs(self, jobNumList, task, queryArguments): """ Submit a batch of jobs from the sandbox Returns: JobInfoMaps { HTCJobID : InfoData,...] Sequence of per job information """ raise AbstractError def checkJobs(self, htcIDs, queryArguments): """ Get the status of a number of jobs Rquired: htcIDs [HTCJobID, ...] Returns: JobInfoMapMaps { HTCJobID : InfoData,...] Sequence of per checked job information maps """ raise AbstractError def getJobsOutput(self, htcIDs): """ Return output of a finished job to the sandbox Rquired: htcIDs [HTCJobID, ...] Returns: ReturnedJobs [HTCJobID,...] Sequence of retrieved jobs """ raise AbstractError def cancelJobs(self, htcIDs): """ Cancel/Abort/Delete a number of jobs Rquired: htcIDs [HTCJobID, ...] Returns: ReturnedJobs [HTCJobID,...] Sequence of removed jobs""" raise AbstractError def getTimings(self): """Return suggested Idle/Active polling interval""" return utils.Result(waitOnIdle = 60, waitBetweenSteps = 10) def getCanSubmit(self): """Return whether submission to this Schedd is possible""" return ( self._adapter.LoggedExecute("which condor_submit").wait(timeout = self._adapterMaxWait) == 0 ) def getSubmitScale(self): """Return number of jobs to submit as one batch""" return self._submitScale def getHTCVersion(self): """Return the version of the attached HTC installation as tuple(X,Y,Z)""" raise AbstractError # internal interfaces for HTC Schedds def getStagingDir(self, htcID = None, taskID = None): """Return path in the Schedd domain where HTC picks up and returns files""" raise AbstractError def cleanStagingDir(self, htcID = None, taskID = None): """Clean path in the Schedd domain where HTC picks up and returns files""" raise AbstractError def _getBaseJDLData(self, task, queryArguments): """Create a sequence of default attributes for a submission JDL""" jdlData = [ '+submitTool = "GridControl (version %s)"' % utils.getVersion(), 'should_transfer_files = YES', 'when_to_transfer_output = ON_EXIT', 'periodic_remove = (JobStatus == 5 && HoldReasonCode != 16)', 'environment = CONDOR_WMS_DASHID=https://%s:/$(Cluster).$(Process)' % self.parentPool.wmsName, 'Universe = %s' % self.parentPool._jobSettings["Universe"], # TODO: Unhack me '+GcID = "%s"' % self.parentPool._createGcId( HTCJobID( gcJobNum = '$(GcJobNum)', gcTaskID = task.taskID, clusterID = '$(Cluster)', procID = '$(Process)', scheddURI = self.getURI(), typed = False) ), '+GcJobNumToWmsID = "$(GcJobNum)@$(Cluster).$(Process)"', '+GcJobNumToGcID = "$(GcJobNum)@$(GcID)"', 'Log = %s' % os.path.join(self.getStagingDir(), 'gcJobs.log'), 'job_ad_information_attrs = %s' %','.join([ arg for arg in queryArguments if arg not in ['JobStatus']]), ] for key in queryArguments: try: # is this a match string? '+JOB_GLIDEIN_Entry_Name = "$$(GLIDEIN_Entry_Name:Unknown)"' -> MATCH_GLIDEIN_Entry_Name = "CMS_T2_DE_RWTH_grid-ce2" && MATCH_EXP_JOB_GLIDEIN_Entry_Name = "CMS_T2_DE_RWTH_grid-ce2" matchKey=re.match("(?:MATCH_EXP_JOB_|MATCH_|JOB_)(.*)",key).group(1) jdlData['Head']['+JOB_%s'%matchKey] = "$$(%s:Unknown)" % matchKey except AttributeError: pass for line in self.parentPool._jobSettings["ClassAd"]: jdlData.append( '+' + line ) for line in self.parentPool._jobSettings["JDL"]: jdlData.append( line ) return jdlData return jdlData def _getRequirementJdlData(self, task, jobNum): """Create JDL attributes corresponding to job requirements""" jdlData = [] requirements = task.getRequirements(jobNum) poolRequMap = self.parentPool.jdlRequirementMap for reqType, reqValue in requirements: # ('WALLTIME', 'CPUTIME', 'MEMORY', 'CPUS', 'BACKEND', 'SITES', 'QUEUES', 'SOFTWARE', 'STORAGE') if reqType == WMS.SITES: (wantSites, vetoSites) = utils.splitBlackWhiteList(reqValue[1]) if "+SITES" in poolRequMap: jdlData.append( '%s = "%s"' % ( poolRequMap["+SITES"][0], poolRequMap["+SITES"][1] % ','.join(wantSites) ) ) if "-SITES" in poolRequMap: jdlData.append( '%s = "%s"' % ( poolRequMap["-SITES"][0], poolRequMap["-SITES"][1] % ','.join(vetoSites) ) ) continue if reqType == WMS.STORAGE: if ("STORAGE" in poolRequMap) and reqValue > 0: jdlData.append( '%s = %s ' % ( poolRequMap["STORAGE"][0], poolRequMap["STORAGE"][1] % ','.join(reqValue) ) ) continue #HACK if reqValue > 0 and WMS.reqTypes[reqType] in poolRequMap: jdlData.append( "%s = %s" % ( poolRequMap[WMS.reqTypes[reqType]][0], poolRequMap[WMS.reqTypes[reqType]][1] % reqValue ) ) continue try: if int(reqValue) <= 0: continue except TypeError: pass self._log(logging.INFO3, "Requirement '%s' cannot be mapped to pool and will be ignored!" % WMS.reqTypes[reqType]) return jdlData # GC internals @classmethod def _initLogger(self): self._logger = logging.getLogger('backend.htcschedd.%s' % self.__name__) self._log = self._logger.log