wmglobalqueue 2.4.5.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- Utils/CPMetrics.py +270 -0
- Utils/CertTools.py +100 -0
- Utils/EmailAlert.py +50 -0
- Utils/ExtendedUnitTestCase.py +62 -0
- Utils/FileTools.py +182 -0
- Utils/IteratorTools.py +80 -0
- Utils/MathUtils.py +31 -0
- Utils/MemoryCache.py +119 -0
- Utils/Patterns.py +24 -0
- Utils/Pipeline.py +137 -0
- Utils/PortForward.py +97 -0
- Utils/ProcFS.py +112 -0
- Utils/ProcessStats.py +194 -0
- Utils/PythonVersion.py +17 -0
- Utils/Signals.py +36 -0
- Utils/TemporaryEnvironment.py +27 -0
- Utils/Throttled.py +227 -0
- Utils/Timers.py +130 -0
- Utils/Timestamps.py +86 -0
- Utils/TokenManager.py +143 -0
- Utils/Tracing.py +60 -0
- Utils/TwPrint.py +98 -0
- Utils/Utilities.py +318 -0
- Utils/__init__.py +11 -0
- Utils/wmcoreDTools.py +707 -0
- WMCore/ACDC/Collection.py +57 -0
- WMCore/ACDC/CollectionTypes.py +12 -0
- WMCore/ACDC/CouchCollection.py +67 -0
- WMCore/ACDC/CouchFileset.py +238 -0
- WMCore/ACDC/CouchService.py +73 -0
- WMCore/ACDC/DataCollectionService.py +485 -0
- WMCore/ACDC/Fileset.py +94 -0
- WMCore/ACDC/__init__.py +11 -0
- WMCore/Algorithms/Alarm.py +39 -0
- WMCore/Algorithms/MathAlgos.py +274 -0
- WMCore/Algorithms/MiscAlgos.py +67 -0
- WMCore/Algorithms/ParseXMLFile.py +115 -0
- WMCore/Algorithms/Permissions.py +27 -0
- WMCore/Algorithms/Singleton.py +58 -0
- WMCore/Algorithms/SubprocessAlgos.py +129 -0
- WMCore/Algorithms/__init__.py +7 -0
- WMCore/Cache/GenericDataCache.py +98 -0
- WMCore/Cache/WMConfigCache.py +572 -0
- WMCore/Cache/__init__.py +0 -0
- WMCore/Configuration.py +659 -0
- WMCore/DAOFactory.py +47 -0
- WMCore/DataStructs/File.py +177 -0
- WMCore/DataStructs/Fileset.py +140 -0
- WMCore/DataStructs/Job.py +182 -0
- WMCore/DataStructs/JobGroup.py +142 -0
- WMCore/DataStructs/JobPackage.py +49 -0
- WMCore/DataStructs/LumiList.py +734 -0
- WMCore/DataStructs/Mask.py +219 -0
- WMCore/DataStructs/MathStructs/ContinuousSummaryHistogram.py +197 -0
- WMCore/DataStructs/MathStructs/DiscreteSummaryHistogram.py +92 -0
- WMCore/DataStructs/MathStructs/SummaryHistogram.py +117 -0
- WMCore/DataStructs/MathStructs/__init__.py +0 -0
- WMCore/DataStructs/Pickleable.py +24 -0
- WMCore/DataStructs/Run.py +256 -0
- WMCore/DataStructs/Subscription.py +175 -0
- WMCore/DataStructs/WMObject.py +47 -0
- WMCore/DataStructs/WorkUnit.py +112 -0
- WMCore/DataStructs/Workflow.py +60 -0
- WMCore/DataStructs/__init__.py +8 -0
- WMCore/Database/CMSCouch.py +1430 -0
- WMCore/Database/ConfigDBMap.py +29 -0
- WMCore/Database/CouchMonitoring.py +450 -0
- WMCore/Database/CouchUtils.py +118 -0
- WMCore/Database/DBCore.py +198 -0
- WMCore/Database/DBCreator.py +113 -0
- WMCore/Database/DBExceptionHandler.py +59 -0
- WMCore/Database/DBFactory.py +117 -0
- WMCore/Database/DBFormatter.py +177 -0
- WMCore/Database/Dialects.py +13 -0
- WMCore/Database/ExecuteDAO.py +327 -0
- WMCore/Database/MongoDB.py +241 -0
- WMCore/Database/MySQL/Destroy.py +42 -0
- WMCore/Database/MySQL/ListUserContent.py +20 -0
- WMCore/Database/MySQL/__init__.py +9 -0
- WMCore/Database/MySQLCore.py +132 -0
- WMCore/Database/Oracle/Destroy.py +56 -0
- WMCore/Database/Oracle/ListUserContent.py +19 -0
- WMCore/Database/Oracle/__init__.py +9 -0
- WMCore/Database/ResultSet.py +44 -0
- WMCore/Database/Transaction.py +91 -0
- WMCore/Database/__init__.py +9 -0
- WMCore/Database/ipy_profile_couch.py +438 -0
- WMCore/GlobalWorkQueue/CherryPyThreads/CleanUpTask.py +29 -0
- WMCore/GlobalWorkQueue/CherryPyThreads/HeartbeatMonitor.py +105 -0
- WMCore/GlobalWorkQueue/CherryPyThreads/LocationUpdateTask.py +28 -0
- WMCore/GlobalWorkQueue/CherryPyThreads/ReqMgrInteractionTask.py +35 -0
- WMCore/GlobalWorkQueue/CherryPyThreads/__init__.py +0 -0
- WMCore/GlobalWorkQueue/__init__.py +0 -0
- WMCore/GroupUser/CouchObject.py +127 -0
- WMCore/GroupUser/Decorators.py +51 -0
- WMCore/GroupUser/Group.py +33 -0
- WMCore/GroupUser/Interface.py +73 -0
- WMCore/GroupUser/User.py +96 -0
- WMCore/GroupUser/__init__.py +11 -0
- WMCore/Lexicon.py +836 -0
- WMCore/REST/Auth.py +202 -0
- WMCore/REST/CherryPyPeriodicTask.py +166 -0
- WMCore/REST/Error.py +333 -0
- WMCore/REST/Format.py +642 -0
- WMCore/REST/HeartbeatMonitorBase.py +90 -0
- WMCore/REST/Main.py +636 -0
- WMCore/REST/Server.py +2435 -0
- WMCore/REST/Services.py +24 -0
- WMCore/REST/Test.py +120 -0
- WMCore/REST/Tools.py +38 -0
- WMCore/REST/Validation.py +250 -0
- WMCore/REST/__init__.py +1 -0
- WMCore/ReqMgr/DataStructs/RequestStatus.py +209 -0
- WMCore/ReqMgr/DataStructs/RequestType.py +13 -0
- WMCore/ReqMgr/DataStructs/__init__.py +0 -0
- WMCore/ReqMgr/__init__.py +1 -0
- WMCore/Services/AlertManager/AlertManagerAPI.py +111 -0
- WMCore/Services/AlertManager/__init__.py +0 -0
- WMCore/Services/CRIC/CRIC.py +238 -0
- WMCore/Services/CRIC/__init__.py +0 -0
- WMCore/Services/DBS/DBS3Reader.py +1044 -0
- WMCore/Services/DBS/DBSConcurrency.py +44 -0
- WMCore/Services/DBS/DBSErrors.py +112 -0
- WMCore/Services/DBS/DBSReader.py +23 -0
- WMCore/Services/DBS/DBSUtils.py +166 -0
- WMCore/Services/DBS/DBSWriterObjects.py +381 -0
- WMCore/Services/DBS/ProdException.py +133 -0
- WMCore/Services/DBS/__init__.py +8 -0
- WMCore/Services/FWJRDB/FWJRDBAPI.py +118 -0
- WMCore/Services/FWJRDB/__init__.py +0 -0
- WMCore/Services/HTTPS/HTTPSAuthHandler.py +66 -0
- WMCore/Services/HTTPS/__init__.py +0 -0
- WMCore/Services/LogDB/LogDB.py +201 -0
- WMCore/Services/LogDB/LogDBBackend.py +191 -0
- WMCore/Services/LogDB/LogDBExceptions.py +11 -0
- WMCore/Services/LogDB/LogDBReport.py +85 -0
- WMCore/Services/LogDB/__init__.py +0 -0
- WMCore/Services/MSPileup/__init__.py +0 -0
- WMCore/Services/MSUtils/MSUtils.py +54 -0
- WMCore/Services/MSUtils/__init__.py +0 -0
- WMCore/Services/McM/McM.py +173 -0
- WMCore/Services/McM/__init__.py +8 -0
- WMCore/Services/MonIT/Grafana.py +133 -0
- WMCore/Services/MonIT/__init__.py +0 -0
- WMCore/Services/PyCondor/PyCondorAPI.py +154 -0
- WMCore/Services/PyCondor/__init__.py +0 -0
- WMCore/Services/ReqMgr/ReqMgr.py +261 -0
- WMCore/Services/ReqMgr/__init__.py +0 -0
- WMCore/Services/ReqMgrAux/ReqMgrAux.py +419 -0
- WMCore/Services/ReqMgrAux/__init__.py +0 -0
- WMCore/Services/RequestDB/RequestDBReader.py +267 -0
- WMCore/Services/RequestDB/RequestDBWriter.py +39 -0
- WMCore/Services/RequestDB/__init__.py +0 -0
- WMCore/Services/Requests.py +624 -0
- WMCore/Services/Rucio/Rucio.py +1290 -0
- WMCore/Services/Rucio/RucioUtils.py +74 -0
- WMCore/Services/Rucio/__init__.py +0 -0
- WMCore/Services/RucioConMon/RucioConMon.py +121 -0
- WMCore/Services/RucioConMon/__init__.py +0 -0
- WMCore/Services/Service.py +400 -0
- WMCore/Services/StompAMQ/__init__.py +0 -0
- WMCore/Services/TagCollector/TagCollector.py +155 -0
- WMCore/Services/TagCollector/XMLUtils.py +98 -0
- WMCore/Services/TagCollector/__init__.py +0 -0
- WMCore/Services/UUIDLib.py +13 -0
- WMCore/Services/UserFileCache/UserFileCache.py +160 -0
- WMCore/Services/UserFileCache/__init__.py +8 -0
- WMCore/Services/WMAgent/WMAgent.py +63 -0
- WMCore/Services/WMAgent/__init__.py +0 -0
- WMCore/Services/WMArchive/CMSSWMetrics.py +526 -0
- WMCore/Services/WMArchive/DataMap.py +463 -0
- WMCore/Services/WMArchive/WMArchive.py +33 -0
- WMCore/Services/WMArchive/__init__.py +0 -0
- WMCore/Services/WMBS/WMBS.py +97 -0
- WMCore/Services/WMBS/__init__.py +0 -0
- WMCore/Services/WMStats/DataStruct/RequestInfoCollection.py +300 -0
- WMCore/Services/WMStats/DataStruct/__init__.py +0 -0
- WMCore/Services/WMStats/WMStatsPycurl.py +145 -0
- WMCore/Services/WMStats/WMStatsReader.py +445 -0
- WMCore/Services/WMStats/WMStatsWriter.py +273 -0
- WMCore/Services/WMStats/__init__.py +0 -0
- WMCore/Services/WMStatsServer/WMStatsServer.py +134 -0
- WMCore/Services/WMStatsServer/__init__.py +0 -0
- WMCore/Services/WorkQueue/WorkQueue.py +492 -0
- WMCore/Services/WorkQueue/__init__.py +0 -0
- WMCore/Services/__init__.py +8 -0
- WMCore/Services/pycurl_manager.py +574 -0
- WMCore/WMBase.py +50 -0
- WMCore/WMConnectionBase.py +164 -0
- WMCore/WMException.py +183 -0
- WMCore/WMExceptions.py +269 -0
- WMCore/WMFactory.py +76 -0
- WMCore/WMInit.py +377 -0
- WMCore/WMLogging.py +104 -0
- WMCore/WMSpec/ConfigSectionTree.py +442 -0
- WMCore/WMSpec/Persistency.py +135 -0
- WMCore/WMSpec/Steps/BuildMaster.py +87 -0
- WMCore/WMSpec/Steps/BuildTools.py +201 -0
- WMCore/WMSpec/Steps/Builder.py +97 -0
- WMCore/WMSpec/Steps/Diagnostic.py +89 -0
- WMCore/WMSpec/Steps/Emulator.py +62 -0
- WMCore/WMSpec/Steps/ExecuteMaster.py +208 -0
- WMCore/WMSpec/Steps/Executor.py +210 -0
- WMCore/WMSpec/Steps/StepFactory.py +213 -0
- WMCore/WMSpec/Steps/TaskEmulator.py +75 -0
- WMCore/WMSpec/Steps/Template.py +204 -0
- WMCore/WMSpec/Steps/Templates/AlcaHarvest.py +76 -0
- WMCore/WMSpec/Steps/Templates/CMSSW.py +613 -0
- WMCore/WMSpec/Steps/Templates/DQMUpload.py +59 -0
- WMCore/WMSpec/Steps/Templates/DeleteFiles.py +70 -0
- WMCore/WMSpec/Steps/Templates/LogArchive.py +84 -0
- WMCore/WMSpec/Steps/Templates/LogCollect.py +105 -0
- WMCore/WMSpec/Steps/Templates/StageOut.py +105 -0
- WMCore/WMSpec/Steps/Templates/__init__.py +10 -0
- WMCore/WMSpec/Steps/WMExecutionFailure.py +21 -0
- WMCore/WMSpec/Steps/__init__.py +8 -0
- WMCore/WMSpec/Utilities.py +63 -0
- WMCore/WMSpec/WMSpecErrors.py +12 -0
- WMCore/WMSpec/WMStep.py +347 -0
- WMCore/WMSpec/WMTask.py +1997 -0
- WMCore/WMSpec/WMWorkload.py +2288 -0
- WMCore/WMSpec/WMWorkloadTools.py +382 -0
- WMCore/WMSpec/__init__.py +9 -0
- WMCore/WorkQueue/DataLocationMapper.py +273 -0
- WMCore/WorkQueue/DataStructs/ACDCBlock.py +47 -0
- WMCore/WorkQueue/DataStructs/Block.py +48 -0
- WMCore/WorkQueue/DataStructs/CouchWorkQueueElement.py +148 -0
- WMCore/WorkQueue/DataStructs/WorkQueueElement.py +274 -0
- WMCore/WorkQueue/DataStructs/WorkQueueElementResult.py +152 -0
- WMCore/WorkQueue/DataStructs/WorkQueueElementsSummary.py +185 -0
- WMCore/WorkQueue/DataStructs/__init__.py +0 -0
- WMCore/WorkQueue/Policy/End/EndPolicyInterface.py +44 -0
- WMCore/WorkQueue/Policy/End/SingleShot.py +22 -0
- WMCore/WorkQueue/Policy/End/__init__.py +32 -0
- WMCore/WorkQueue/Policy/PolicyInterface.py +17 -0
- WMCore/WorkQueue/Policy/Start/Block.py +258 -0
- WMCore/WorkQueue/Policy/Start/Dataset.py +180 -0
- WMCore/WorkQueue/Policy/Start/MonteCarlo.py +131 -0
- WMCore/WorkQueue/Policy/Start/ResubmitBlock.py +171 -0
- WMCore/WorkQueue/Policy/Start/StartPolicyInterface.py +316 -0
- WMCore/WorkQueue/Policy/Start/__init__.py +34 -0
- WMCore/WorkQueue/Policy/__init__.py +57 -0
- WMCore/WorkQueue/WMBSHelper.py +772 -0
- WMCore/WorkQueue/WorkQueue.py +1237 -0
- WMCore/WorkQueue/WorkQueueBackend.py +750 -0
- WMCore/WorkQueue/WorkQueueBase.py +39 -0
- WMCore/WorkQueue/WorkQueueExceptions.py +44 -0
- WMCore/WorkQueue/WorkQueueReqMgrInterface.py +278 -0
- WMCore/WorkQueue/WorkQueueUtils.py +130 -0
- WMCore/WorkQueue/__init__.py +13 -0
- WMCore/Wrappers/JsonWrapper/JSONThunker.py +342 -0
- WMCore/Wrappers/JsonWrapper/__init__.py +7 -0
- WMCore/Wrappers/__init__.py +6 -0
- WMCore/__init__.py +10 -0
- wmglobalqueue-2.4.5.1.data/data/bin/wmc-dist-patch +15 -0
- wmglobalqueue-2.4.5.1.data/data/bin/wmc-dist-unpatch +8 -0
- wmglobalqueue-2.4.5.1.data/data/bin/wmc-httpd +3 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/.couchapprc +1 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/README.md +40 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/_attachments/index.html +264 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/_attachments/js/ElementInfoByWorkflow.js +96 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/_attachments/js/StuckElementInfo.js +57 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/_attachments/js/WorkloadInfoTable.js +80 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/_attachments/js/dataTable.js +70 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/_attachments/js/namespace.js +23 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/_attachments/style/main.css +75 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/couchapp.json +4 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/filters/childQueueFilter.js +13 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/filters/filterDeletedDocs.js +3 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/filters/queueFilter.js +11 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/language +1 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/lib/mustache.js +333 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/lib/validate.js +27 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/lib/workqueue_utils.js +61 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/lists/elementsDetail.js +28 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/lists/filter.js +86 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/lists/stuckElements.js +38 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/lists/workRestrictions.js +153 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/lists/workflowSummary.js +28 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/rewrites.json +73 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/shows/redirect.js +23 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/shows/status.js +40 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/templates/ElementSummaryByWorkflow.html +27 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/templates/StuckElementSummary.html +26 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/templates/TaskStatus.html +23 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/templates/WorkflowSummary.html +27 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/templates/partials/workqueue-common-lib.html +2 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/templates/partials/yui-lib-remote.html +16 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/templates/partials/yui-lib.html +18 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/updates/in-place.js +50 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/validate_doc_update.js +8 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/vendor/couchapp/_attachments/jquery.couch.app.js +235 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/vendor/couchapp/_attachments/jquery.pathbinder.js +173 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/activeData/map.js +8 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/activeData/reduce.js +2 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/activeParentData/map.js +8 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/activeParentData/reduce.js +2 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/activePileupData/map.js +8 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/activePileupData/reduce.js +2 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/analyticsData/map.js +11 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/analyticsData/reduce.js +1 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/availableByPriority/map.js +6 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/conflicts/map.js +5 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/elements/map.js +5 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/elementsByData/map.js +8 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/elementsByParent/map.js +8 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/elementsByParentData/map.js +8 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/elementsByPileupData/map.js +8 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/elementsByStatus/map.js +8 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/elementsBySubscription/map.js +6 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/elementsByWorkflow/map.js +8 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/elementsByWorkflow/reduce.js +3 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/elementsDetailByWorkflowAndStatus/map.js +26 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobInjectStatusByRequest/map.js +10 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobInjectStatusByRequest/reduce.js +1 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobStatusByRequest/map.js +6 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobStatusByRequest/reduce.js +1 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobsByChildQueueAndPriority/map.js +6 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobsByChildQueueAndPriority/reduce.js +1 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobsByChildQueueAndStatus/map.js +6 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobsByChildQueueAndStatus/reduce.js +1 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobsByRequest/map.js +6 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobsByRequest/reduce.js +1 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobsByStatus/map.js +6 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobsByStatus/reduce.js +1 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobsByStatusAndPriority/map.js +6 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/jobsByStatusAndPriority/reduce.js +1 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/openRequests/map.js +6 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/recent-items/map.js +5 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/siteWhitelistByRequest/map.js +6 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/siteWhitelistByRequest/reduce.js +1 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/specsByWorkflow/map.js +5 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/stuckElements/map.js +38 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/wmbsInjectStatusByRequest/map.js +12 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/wmbsInjectStatusByRequest/reduce.js +3 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/wmbsUrl/map.js +6 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/wmbsUrl/reduce.js +2 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/wmbsUrlByRequest/map.js +6 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/wmbsUrlByRequest/reduce.js +2 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/workflowSummary/map.js +9 -0
- wmglobalqueue-2.4.5.1.data/data/data/couchapps/WorkQueue/views/workflowSummary/reduce.js +10 -0
- wmglobalqueue-2.4.5.1.dist-info/METADATA +26 -0
- wmglobalqueue-2.4.5.1.dist-info/RECORD +347 -0
- wmglobalqueue-2.4.5.1.dist-info/WHEEL +5 -0
- wmglobalqueue-2.4.5.1.dist-info/licenses/LICENSE +202 -0
- wmglobalqueue-2.4.5.1.dist-info/licenses/NOTICE +16 -0
- wmglobalqueue-2.4.5.1.dist-info/top_level.txt +2 -0
Utils/ProcessStats.py
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
File : ProcessStats.py
|
|
5
|
+
Author : Valentin Kuznetsov <vkuznet AT gmail dot com>
|
|
6
|
+
Description: This module provide ability to get detailed information about
|
|
7
|
+
certain UNIX process, referred by PID. It is based on psutil and threading
|
|
8
|
+
python modules. It can be used in any web/data-server to get live status
|
|
9
|
+
about the running server. Here is an example
|
|
10
|
+
|
|
11
|
+
.. doctest:
|
|
12
|
+
|
|
13
|
+
from Utils.procstats import status threadStack
|
|
14
|
+
|
|
15
|
+
class SomeServer(object):
|
|
16
|
+
def __init__(self):
|
|
17
|
+
self.time0 = time.time()
|
|
18
|
+
def status(self):
|
|
19
|
+
sdict = {'server': processStatus()}
|
|
20
|
+
sdict['server'].update({'uptime': time.time()-self.time0})
|
|
21
|
+
sdict.update(threadStack())
|
|
22
|
+
return sdict
|
|
23
|
+
|
|
24
|
+
The exposed status endpoint will return a json dictionary containing server
|
|
25
|
+
upteima and cpu/mem/threads information.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
# system modules
|
|
29
|
+
import os
|
|
30
|
+
import sys
|
|
31
|
+
import json
|
|
32
|
+
import time
|
|
33
|
+
import argparse
|
|
34
|
+
import threading
|
|
35
|
+
import traceback
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
import psutil
|
|
39
|
+
except ImportError:
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
def _baseProcessStatusFormat(pid=None):
|
|
43
|
+
if not pid:
|
|
44
|
+
pid = os.getpid()
|
|
45
|
+
ttime = time.localtime()
|
|
46
|
+
tstamp = time.strftime('%d/%b/%Y:%H:%M:%S', ttime)
|
|
47
|
+
return {'pid': pid, 'timestamp': tstamp, 'time': time.time()}
|
|
48
|
+
|
|
49
|
+
def processStatus(pid=None):
|
|
50
|
+
"Return status of the process in a dictionary format"
|
|
51
|
+
|
|
52
|
+
pdict = _baseProcessStatusFormat(pid)
|
|
53
|
+
if 'psutil' not in sys.modules:
|
|
54
|
+
return pdict
|
|
55
|
+
proc = psutil.Process(pid)
|
|
56
|
+
pdict.update(proc.as_dict())
|
|
57
|
+
return pdict
|
|
58
|
+
|
|
59
|
+
def processStatusDict(pid=None):
|
|
60
|
+
"Return status of the process in a dictionary format"
|
|
61
|
+
pdict = _baseProcessStatusFormat(pid)
|
|
62
|
+
if 'psutil' not in sys.modules:
|
|
63
|
+
return pdict
|
|
64
|
+
proc = psutil.Process(pid)
|
|
65
|
+
pdict.update({"cpu_times": dict(proc.cpu_times()._asdict())})
|
|
66
|
+
pdict.update({"cpu_percent": proc.cpu_percent(interval=1.0)})
|
|
67
|
+
pdict.update({"cpu_num": proc.cpu_num()})
|
|
68
|
+
pdict.update({"memory_full_info": dict(proc.memory_full_info()._asdict())})
|
|
69
|
+
pdict.update({"memory_percent": proc.memory_percent()})
|
|
70
|
+
return pdict
|
|
71
|
+
|
|
72
|
+
def threadStack():
|
|
73
|
+
"""
|
|
74
|
+
Return context of all threads in dictionary format where individual
|
|
75
|
+
thread information stored in its own dicts and all threads are groupped
|
|
76
|
+
into threads list. Code based on example from StackOverflow:
|
|
77
|
+
http://stackoverflow.com/questions/132058/showing-the-stack-trace-from-a-running-python-application
|
|
78
|
+
"""
|
|
79
|
+
tdict = {}
|
|
80
|
+
id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
|
|
81
|
+
threads = []
|
|
82
|
+
for tid, stack in list(sys._current_frames().items()):
|
|
83
|
+
tdict = {"thead": id2name.get(tid, ""), "thead_id": tid}
|
|
84
|
+
stacklist = []
|
|
85
|
+
for filename, lineno, name, line in traceback.extract_stack(stack):
|
|
86
|
+
sdict = dict(filename=filename, line_number=lineno, name=name, line=line)
|
|
87
|
+
stacklist.append(sdict)
|
|
88
|
+
tdict.update({"stack": stacklist})
|
|
89
|
+
threads.append(tdict)
|
|
90
|
+
return dict(threads=threads)
|
|
91
|
+
|
|
92
|
+
def processThreadsInfo(pid):
|
|
93
|
+
"""
|
|
94
|
+
Provides information about process and its threads using psutils
|
|
95
|
+
:param pid: process PID, string
|
|
96
|
+
:return: dictionary statistics about process and threads
|
|
97
|
+
"""
|
|
98
|
+
try:
|
|
99
|
+
process = psutil.Process(pid)
|
|
100
|
+
|
|
101
|
+
# Get overall process details
|
|
102
|
+
process_info = {
|
|
103
|
+
"process_name": process.name(),
|
|
104
|
+
"pid": process.pid,
|
|
105
|
+
"status": process.status(),
|
|
106
|
+
"ppid": process.ppid(), # Parent Process ID
|
|
107
|
+
"is_running":process.is_running(), # NOTE: Zombie processes could also be reported as running
|
|
108
|
+
"cmdline": process.cmdline(), # Full command-line arguments
|
|
109
|
+
"cpu_usage_percent": process.cpu_percent(interval=1.0), # CPU usage in percentage
|
|
110
|
+
"memory_usage_percent": process.memory_percent(), # RAM usage in percentage
|
|
111
|
+
"memory_rss": process.memory_info().rss, # Resident Set Size (bytes)
|
|
112
|
+
"num_open_files": len(process.open_files()), # Number of open file descriptors
|
|
113
|
+
"num_connections": len(process.connections()), # Number of active connections
|
|
114
|
+
"threads": []
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# Iterate over threads to get per-thread details
|
|
118
|
+
for thread in process.threads():
|
|
119
|
+
if str(pid) == str(thread.id):
|
|
120
|
+
continue
|
|
121
|
+
thread_id = thread.id
|
|
122
|
+
|
|
123
|
+
thread_info = {
|
|
124
|
+
"thread_id": thread_id,
|
|
125
|
+
"user_time": thread.user_time, # CPU time spent in user mode
|
|
126
|
+
"system_time": thread.system_time, # CPU time spent in system mode
|
|
127
|
+
"cpu_usage_percent": None,
|
|
128
|
+
"memory_usage_bytes": None,
|
|
129
|
+
"num_open_files": None,
|
|
130
|
+
"num_connections": None,
|
|
131
|
+
"is_running":process.is_running(), # NOTE: Zombie processes could also be reported as running
|
|
132
|
+
"status": "unknown",
|
|
133
|
+
"name": "thread"
|
|
134
|
+
}
|
|
135
|
+
if str(pid) == str(thread_id):
|
|
136
|
+
thread_info['name'] = "process"
|
|
137
|
+
|
|
138
|
+
# Try getting thread status
|
|
139
|
+
try:
|
|
140
|
+
thread_status = psutil.Process(thread_id).status()
|
|
141
|
+
thread_info["status"] = thread_status
|
|
142
|
+
except psutil.NoSuchProcess:
|
|
143
|
+
thread_info["status"] = psutil.STATUS_ZOMBIE
|
|
144
|
+
|
|
145
|
+
# Try getting per-thread CPU usage
|
|
146
|
+
try:
|
|
147
|
+
thread_info["cpu_usage_percent"] = psutil.Process(thread_id).cpu_percent(interval=1.0)
|
|
148
|
+
except Exception:
|
|
149
|
+
thread_info["cpu_usage_percent"] = "N/A"
|
|
150
|
+
|
|
151
|
+
# Try getting per-thread memory usage
|
|
152
|
+
try:
|
|
153
|
+
thread_info["memory_usage_bytes"] = psutil.Process(thread_id).memory_info().rss
|
|
154
|
+
except Exception:
|
|
155
|
+
thread_info["memory_usage_bytes"] = "N/A"
|
|
156
|
+
|
|
157
|
+
# Try getting per-thread open file descriptors
|
|
158
|
+
try:
|
|
159
|
+
thread_info["num_open_files"] = len(psutil.Process(thread_id).open_files())
|
|
160
|
+
except Exception:
|
|
161
|
+
thread_info["num_open_files"] = "N/A"
|
|
162
|
+
|
|
163
|
+
# Try getting per-thread open connections
|
|
164
|
+
try:
|
|
165
|
+
thread_info["num_connections"] = len(psutil.Process(thread_id).connections())
|
|
166
|
+
except Exception:
|
|
167
|
+
thread_info["num_connections"] = "N/A"
|
|
168
|
+
|
|
169
|
+
# Append to process_info
|
|
170
|
+
process_info["threads"].append(thread_info)
|
|
171
|
+
|
|
172
|
+
return process_info
|
|
173
|
+
except psutil.NoSuchProcess:
|
|
174
|
+
return {"error": f"No process found with PID {pid}"}
|
|
175
|
+
except Exception as e:
|
|
176
|
+
return {"error": str(e)}
|
|
177
|
+
|
|
178
|
+
def main():
|
|
179
|
+
"Main function to use this module as a stand-along script."
|
|
180
|
+
parser = argparse.ArgumentParser(prog='PROG')
|
|
181
|
+
parser.add_argument("--pid", action="store", dest="pid", help="process id")
|
|
182
|
+
opts = parser.parse_args()
|
|
183
|
+
|
|
184
|
+
pdict = processStatus(int(opts.pid))
|
|
185
|
+
pdict.update(threadStack())
|
|
186
|
+
print(f"Process status for {opts.pid}")
|
|
187
|
+
print(json.dumps(pdict, indent=4))
|
|
188
|
+
|
|
189
|
+
processInfo = processThreadsInfo(int(opts.pid))
|
|
190
|
+
print(f"Process/threads status for {opts.pid}")
|
|
191
|
+
print(json.dumps(processInfo, indent=4))
|
|
192
|
+
|
|
193
|
+
if __name__ == '__main__':
|
|
194
|
+
main()
|
Utils/PythonVersion.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Easily get the version of the python interpreter at runtime
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
# Jenkins CI
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
import pickle
|
|
9
|
+
|
|
10
|
+
PY3 = sys.version_info[0] == 3
|
|
11
|
+
PY2 = sys.version_info[0] == 2
|
|
12
|
+
|
|
13
|
+
# We need to keep compatibility between multiple python versions
|
|
14
|
+
# Further details at: https://github.com/dmwm/WMCore/pull/10726
|
|
15
|
+
# For PY2: set highest protocol to 2
|
|
16
|
+
# For PY3: set highest protocol to 4 (compatible with python3.6 and python3.8)
|
|
17
|
+
HIGHEST_PICKLE_PROTOCOL = pickle.HIGHEST_PROTOCOL if PY2 else 4
|
Utils/Signals.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
#-*- coding: utf-8 -*-
|
|
3
|
+
#pylint: disable=
|
|
4
|
+
"""
|
|
5
|
+
File : Signals.py
|
|
6
|
+
Author : Valentin Kuznetsov <vkuznet AT gmail dot com>
|
|
7
|
+
Description: This module provide set of hooks to running program
|
|
8
|
+
The dumpthreads callback function will be called every time when
|
|
9
|
+
running program will receive SIGUSR1 signal. E.g., in unix shell
|
|
10
|
+
just do
|
|
11
|
+
shell# kill -s SIGUSR1 <pid>
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
# system modules
|
|
15
|
+
import sys
|
|
16
|
+
import signal
|
|
17
|
+
import threading
|
|
18
|
+
import traceback
|
|
19
|
+
|
|
20
|
+
def dumpthreads(isignal, iframe):
|
|
21
|
+
"""
|
|
22
|
+
Dump context of all threads upon given signal
|
|
23
|
+
http://stackoverflow.com/questions/132058/showing-the-stack-trace-from-a-running-python-application
|
|
24
|
+
"""
|
|
25
|
+
print("DAS stack, signal=%s, frame=%s" % (isignal, iframe))
|
|
26
|
+
id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
|
|
27
|
+
code = []
|
|
28
|
+
for tid, stack in list(sys._current_frames().items()):
|
|
29
|
+
code.append("\n# Thread: %s(%d)" % (id2name.get(tid,""), tid))
|
|
30
|
+
for filename, lineno, name, line in traceback.extract_stack(stack):
|
|
31
|
+
code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
|
|
32
|
+
if line:
|
|
33
|
+
code.append(" %s" % (line.strip()))
|
|
34
|
+
print("\n".join(code))
|
|
35
|
+
|
|
36
|
+
signal.signal(signal.SIGUSR1, dumpthreads)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#! /usr/bin/env python
|
|
2
|
+
|
|
3
|
+
import contextlib
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@contextlib.contextmanager
|
|
8
|
+
def tmpEnv(**environ):
|
|
9
|
+
"""
|
|
10
|
+
Temporarily set the process environment variables.
|
|
11
|
+
|
|
12
|
+
>>> with tmpEnv(PLUGINS_DIR=u'test/plugins'):
|
|
13
|
+
... "PLUGINS_DIR" in os.environ
|
|
14
|
+
True
|
|
15
|
+
|
|
16
|
+
>>> "PLUGINS_DIR" in os.environ
|
|
17
|
+
False
|
|
18
|
+
|
|
19
|
+
:param environ: Environment variables to set
|
|
20
|
+
"""
|
|
21
|
+
oldEnviron = dict(os.environ)
|
|
22
|
+
os.environ.update(environ)
|
|
23
|
+
try:
|
|
24
|
+
yield
|
|
25
|
+
finally:
|
|
26
|
+
os.environ.clear()
|
|
27
|
+
os.environ.update(oldEnviron)
|
Utils/Throttled.py
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# pylint: disable-msg=C0103,C0111,W0212
|
|
2
|
+
"""
|
|
3
|
+
Throttled module defines necessary classes to throttle context.
|
|
4
|
+
Should be used in Web server APIs which wants to throttle clients.
|
|
5
|
+
|
|
6
|
+
The counter based approach, implemented in _ThrottleCounter class,
|
|
7
|
+
defines throttling on total number of user access to the underlying
|
|
8
|
+
app. But its counter depends on time execution of the API.
|
|
9
|
+
|
|
10
|
+
The time based approach, implemented in _ThrottleTimeCount class,
|
|
11
|
+
count total number of user accesses within specified time interval
|
|
12
|
+
regardless of time execution of the underlying API(s).
|
|
13
|
+
|
|
14
|
+
Here is an example how to use this module/classes:
|
|
15
|
+
|
|
16
|
+
# counter based throttline
|
|
17
|
+
|
|
18
|
+
from Utils.Throttling import UserThrottle
|
|
19
|
+
thr = UserThrottle(limit=5) # adjust threshold limit here
|
|
20
|
+
@thr.make_throttled()
|
|
21
|
+
def api():
|
|
22
|
+
# define your api logic here
|
|
23
|
+
|
|
24
|
+
# time based throttling
|
|
25
|
+
|
|
26
|
+
from Utils.Throttling import UserThrottleTime
|
|
27
|
+
thr = UserThrottleTime(limit=5) # adjust threshold limit here
|
|
28
|
+
@thr.make_throttled(trange=2) # adjust trange (in sec) here
|
|
29
|
+
def api():
|
|
30
|
+
# define your api logic here
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# standard modules
|
|
36
|
+
import time
|
|
37
|
+
from builtins import object
|
|
38
|
+
import logging
|
|
39
|
+
import threading
|
|
40
|
+
|
|
41
|
+
# cherrypy modules
|
|
42
|
+
import cherrypy
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class _ThrottleCounter(object):
|
|
46
|
+
"""
|
|
47
|
+
_ThrottleCounter class defines throttle parameter and
|
|
48
|
+
enter/exit methods to work with `with` context.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(self, throttle, user, debug=False):
|
|
52
|
+
self.throttle = throttle
|
|
53
|
+
self.user = user
|
|
54
|
+
if debug:
|
|
55
|
+
self.throttle.logger.setLevel(logging.DEBUG)
|
|
56
|
+
|
|
57
|
+
def __enter__(self):
|
|
58
|
+
"Define enter method for `with` context"
|
|
59
|
+
ctr = self.throttle._incUser(self.user)
|
|
60
|
+
msg = "Entering throttled function with counter %d for user %s" \
|
|
61
|
+
% (ctr, self.user)
|
|
62
|
+
self.throttle.logger.debug(msg)
|
|
63
|
+
if ctr > self.throttle.getLimit():
|
|
64
|
+
msg = "The current number of active operations for this resource"
|
|
65
|
+
msg += " exceeds the limit of %d for user %s" \
|
|
66
|
+
% (self.throttle.getLimit(), self.user)
|
|
67
|
+
raise cherrypy.HTTPError("429 Too Many Requests", msg)
|
|
68
|
+
|
|
69
|
+
def __exit__(self, exc_type, exc_value, exc_traceback):
|
|
70
|
+
"Define exit method for `with` context"
|
|
71
|
+
ctr = self.throttle._decUser(self.user)
|
|
72
|
+
msg = "Exiting throttled function with counter %d for user %s" \
|
|
73
|
+
% (ctr, self.user)
|
|
74
|
+
self.throttle.logger.debug(msg)
|
|
75
|
+
|
|
76
|
+
class _ThrottleTimeCounter(object):
|
|
77
|
+
"""
|
|
78
|
+
_ThrottleTimeCounter class defines time range throttled mechanism
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
def __init__(self, throttle, user, trange):
|
|
82
|
+
self.throttle = throttle
|
|
83
|
+
self.user = user
|
|
84
|
+
self.trange = trange
|
|
85
|
+
|
|
86
|
+
def __enter__(self):
|
|
87
|
+
"Define enter method for `with` context"
|
|
88
|
+
ctr = self.throttle._incUser(self.user, self.trange)
|
|
89
|
+
if ctr > self.throttle.getLimit():
|
|
90
|
+
self.throttle._decUser(self.user)
|
|
91
|
+
msg = "The current number of active operations for this resource"
|
|
92
|
+
msg += " exceeds the limit of %d for user %s in last %s sec" \
|
|
93
|
+
% (self.throttle.getLimit(), self.user, self.trange)
|
|
94
|
+
raise cherrypy.HTTPError("429 Too Many Requests", msg)
|
|
95
|
+
|
|
96
|
+
def __exit__(self, exc_type, exc_value, exc_traceback):
|
|
97
|
+
"Define exit method for `with` context"
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class UserThrottleTime(object):
|
|
102
|
+
"""
|
|
103
|
+
UserThrottle class defines how to handle throttle time range based mechanism.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
def __init__(self, limit=3):
|
|
107
|
+
self.lock = threading.Lock()
|
|
108
|
+
self.users = {}
|
|
109
|
+
self.users_time = {}
|
|
110
|
+
self.limit = limit
|
|
111
|
+
|
|
112
|
+
def getLimit(self):
|
|
113
|
+
"Return throttle limit"
|
|
114
|
+
return self.limit
|
|
115
|
+
|
|
116
|
+
def throttleContext(self, user, trange=60):
|
|
117
|
+
"""
|
|
118
|
+
Define throttle context via _ThrottleTimeCounter class
|
|
119
|
+
which will count number of requests given user made in trange
|
|
120
|
+
:param user: user name
|
|
121
|
+
:param trange: time range while keep user access history
|
|
122
|
+
"""
|
|
123
|
+
return _ThrottleTimeCounter(self, user, trange)
|
|
124
|
+
|
|
125
|
+
def make_throttled(self, trange=60):
|
|
126
|
+
"decorator for throttled context"
|
|
127
|
+
def throttled_decorator(fn):
|
|
128
|
+
def throttled_wrapped_function(*args, **kw):
|
|
129
|
+
username = cherrypy.request.user.get('login', 'Unknown') \
|
|
130
|
+
if hasattr(cherrypy.request, 'user') else 'Unknown'
|
|
131
|
+
with self.throttleContext(username, trange):
|
|
132
|
+
return fn(*args, **kw)
|
|
133
|
+
return throttled_wrapped_function
|
|
134
|
+
return throttled_decorator
|
|
135
|
+
|
|
136
|
+
def reset(self, user):
|
|
137
|
+
"Reset user activities, i.e. access counter and last time of access"
|
|
138
|
+
self.users[user] = 0
|
|
139
|
+
self.users_time[user] = time.time()
|
|
140
|
+
|
|
141
|
+
def _incUser(self, user, trange=60):
|
|
142
|
+
"increment user count"
|
|
143
|
+
with self.lock:
|
|
144
|
+
self.users.setdefault(user, 0)
|
|
145
|
+
last_time = self.users_time.setdefault(user, time.time())
|
|
146
|
+
# increase counter within our trange
|
|
147
|
+
if abs(time.time()-last_time) < trange:
|
|
148
|
+
self.users[user] += 1
|
|
149
|
+
else:
|
|
150
|
+
self.reset(user)
|
|
151
|
+
return self.users[user]
|
|
152
|
+
|
|
153
|
+
def _decUser(self, user, trange=60):
|
|
154
|
+
"decrecrement user count"
|
|
155
|
+
with self.lock:
|
|
156
|
+
# decrease counter outside of our trange
|
|
157
|
+
last_time = self.users_time[user]
|
|
158
|
+
if abs(time.time()-last_time) < trange:
|
|
159
|
+
# gradually decrease user counter based on his/her elapsed time
|
|
160
|
+
step = int(trange - abs(time.time()-last_time))
|
|
161
|
+
self.users[user] -= step
|
|
162
|
+
if self.users[user] < 0:
|
|
163
|
+
self.reset(user)
|
|
164
|
+
else:
|
|
165
|
+
self.reset(user)
|
|
166
|
+
|
|
167
|
+
class UserThrottle(object):
|
|
168
|
+
"""
|
|
169
|
+
UserThrottle class defines throttle counter based mechanism.
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
def __init__(self, limit=3):
|
|
173
|
+
self.lock = threading.Lock()
|
|
174
|
+
self.tls = threading.local()
|
|
175
|
+
self.users = {}
|
|
176
|
+
self.limit = limit
|
|
177
|
+
self.logger = logging.getLogger("WMCore.UserThrottle")
|
|
178
|
+
|
|
179
|
+
def getLimit(self):
|
|
180
|
+
"Return throttle limit"
|
|
181
|
+
return self.limit
|
|
182
|
+
|
|
183
|
+
def throttleContext(self, user, debug=False):
|
|
184
|
+
"defint throttle context"
|
|
185
|
+
self.users.setdefault(user, 0)
|
|
186
|
+
return _ThrottleCounter(self, user, debug)
|
|
187
|
+
|
|
188
|
+
def make_throttled(self, debug=False):
|
|
189
|
+
"""
|
|
190
|
+
decorator for throttled context
|
|
191
|
+
"""
|
|
192
|
+
def throttled_decorator(fn):
|
|
193
|
+
"""
|
|
194
|
+
A decorator
|
|
195
|
+
"""
|
|
196
|
+
def throttled_wrapped_function(*args, **kw):
|
|
197
|
+
"""
|
|
198
|
+
A wrapped function.
|
|
199
|
+
"""
|
|
200
|
+
username = cherrypy.request.user.get('login', 'Unknown') \
|
|
201
|
+
if hasattr(cherrypy.request, 'user') else 'Unknown'
|
|
202
|
+
with self.throttleContext(username, debug):
|
|
203
|
+
return fn(*args, **kw)
|
|
204
|
+
return throttled_wrapped_function
|
|
205
|
+
return throttled_decorator
|
|
206
|
+
|
|
207
|
+
def _incUser(self, user):
|
|
208
|
+
"increment user count"
|
|
209
|
+
retval = 0
|
|
210
|
+
with self.lock:
|
|
211
|
+
retval = self.users[user]
|
|
212
|
+
if getattr(self.tls, 'count', None) is None:
|
|
213
|
+
self.tls.count = 0
|
|
214
|
+
self.tls.count += 1
|
|
215
|
+
if self.tls.count == 1:
|
|
216
|
+
self.users[user] = retval + 1
|
|
217
|
+
return retval
|
|
218
|
+
|
|
219
|
+
def _decUser(self, user):
|
|
220
|
+
"decrecrement user count"
|
|
221
|
+
retval = 0
|
|
222
|
+
with self.lock:
|
|
223
|
+
retval = self.users[user]
|
|
224
|
+
self.tls.count -= 1
|
|
225
|
+
if self.tls.count == 0:
|
|
226
|
+
self.users[user] = retval - 1
|
|
227
|
+
return retval
|
Utils/Timers.py
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
Utilities related to timing and performance testing
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from builtins import object
|
|
7
|
+
import logging
|
|
8
|
+
import time
|
|
9
|
+
import calendar
|
|
10
|
+
from datetime import tzinfo, timedelta
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def gmtimeSeconds():
|
|
14
|
+
"""
|
|
15
|
+
Return GMT time in seconds
|
|
16
|
+
"""
|
|
17
|
+
return int(time.mktime(time.gmtime()))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def encodeTimestamp(secs):
|
|
21
|
+
"""
|
|
22
|
+
Encode second since epoch to a string GMT timezone representation
|
|
23
|
+
:param secs: input timestamp value (either int or float) in seconds since epoch
|
|
24
|
+
|
|
25
|
+
:return: time string in GMT timezone representation
|
|
26
|
+
"""
|
|
27
|
+
if not isinstance(secs, (int, float)):
|
|
28
|
+
raise Exception("Wrong input, should be seconds since epoch either int or float value")
|
|
29
|
+
return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(int(secs)))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def decodeTimestamp(timeString):
|
|
33
|
+
"""
|
|
34
|
+
Decode timestamps in provided document
|
|
35
|
+
:param timeString: timestamp string represention in GMT timezone, see encodeTimestamp
|
|
36
|
+
|
|
37
|
+
:return: seconds since ecouch in GMT timezone
|
|
38
|
+
"""
|
|
39
|
+
if not isinstance(timeString, str):
|
|
40
|
+
raise Exception("Wrong input, should be time string in GMT timezone representation")
|
|
41
|
+
return calendar.timegm(time.strptime(timeString, "%Y-%m-%dT%H:%M:%SZ"))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def timeFunction(func):
|
|
45
|
+
"""
|
|
46
|
+
source: https://www.andreas-jung.com/contents/a-python-decorator-for-measuring-the-execution-time-of-methods
|
|
47
|
+
|
|
48
|
+
Decorator function to measure how long a method/function takes to run
|
|
49
|
+
It returns a tuple with:
|
|
50
|
+
* wall clock time spent
|
|
51
|
+
* returned result of the function
|
|
52
|
+
* the function name
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def wrapper(*arg, **kw):
|
|
56
|
+
t1 = time.time()
|
|
57
|
+
res = func(*arg, **kw)
|
|
58
|
+
t2 = time.time()
|
|
59
|
+
return round((t2 - t1), 4), res, func.__name__
|
|
60
|
+
|
|
61
|
+
return wrapper
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class CodeTimer(object):
|
|
65
|
+
"""
|
|
66
|
+
A context manager for timing function calls.
|
|
67
|
+
Adapted from https://www.blog.pythonlibrary.org/2016/05/24/python-101-an-intro-to-benchmarking-your-code/
|
|
68
|
+
|
|
69
|
+
Use like
|
|
70
|
+
|
|
71
|
+
with CodeTimer(label='Doing something'):
|
|
72
|
+
do_something()
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
def __init__(self, label='The function', logger=None):
|
|
76
|
+
self.start = time.time()
|
|
77
|
+
self.label = label
|
|
78
|
+
self.logger = logger or logging.getLogger()
|
|
79
|
+
|
|
80
|
+
def __enter__(self):
|
|
81
|
+
return self
|
|
82
|
+
|
|
83
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
84
|
+
end = time.time()
|
|
85
|
+
runtime = round((end - self.start), 3)
|
|
86
|
+
self.logger.info(f"{self.label} took {runtime} seconds to complete")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class LocalTimezone(tzinfo):
|
|
90
|
+
"""
|
|
91
|
+
A required python 2 class to determine current timezone for formatting rfc3339 timestamps
|
|
92
|
+
Required for sending alerts to the MONIT AlertManager
|
|
93
|
+
Can be removed once WMCore starts using python3
|
|
94
|
+
|
|
95
|
+
Details of class can be found at: https://docs.python.org/2/library/datetime.html#tzinfo-objects
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
def __init__(self):
|
|
99
|
+
super(LocalTimezone, self).__init__()
|
|
100
|
+
self.ZERO = timedelta(0)
|
|
101
|
+
self.STDOFFSET = timedelta(seconds=-time.timezone)
|
|
102
|
+
if time.daylight:
|
|
103
|
+
self.DSTOFFSET = timedelta(seconds=-time.altzone)
|
|
104
|
+
else:
|
|
105
|
+
self.DSTOFFSET = self.STDOFFSET
|
|
106
|
+
|
|
107
|
+
self.DSTDIFF = self.DSTOFFSET - self.STDOFFSET
|
|
108
|
+
|
|
109
|
+
def utcoffset(self, dt):
|
|
110
|
+
if self._isdst(dt):
|
|
111
|
+
return self.DSTOFFSET
|
|
112
|
+
else:
|
|
113
|
+
return self.STDOFFSET
|
|
114
|
+
|
|
115
|
+
def dst(self, dt):
|
|
116
|
+
if self._isdst(dt):
|
|
117
|
+
return self.DSTDIFF
|
|
118
|
+
else:
|
|
119
|
+
return self.ZERO
|
|
120
|
+
|
|
121
|
+
def tzname(self, dt):
|
|
122
|
+
return time.tzname[self._isdst(dt)]
|
|
123
|
+
|
|
124
|
+
def _isdst(self, dt):
|
|
125
|
+
tt = (dt.year, dt.month, dt.day,
|
|
126
|
+
dt.hour, dt.minute, dt.second,
|
|
127
|
+
dt.weekday(), 0, 0)
|
|
128
|
+
stamp = time.mktime(tt)
|
|
129
|
+
tt = time.localtime(stamp)
|
|
130
|
+
return tt.tm_isdst > 0
|