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/IteratorTools.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#! /usr/bin/env python
|
|
2
|
+
|
|
3
|
+
from builtins import str, map
|
|
4
|
+
import collections.abc
|
|
5
|
+
from itertools import islice, chain, groupby
|
|
6
|
+
|
|
7
|
+
def grouper(iterable, n):
|
|
8
|
+
"""
|
|
9
|
+
:param iterable: List of other iterable to slice
|
|
10
|
+
:type: iterable
|
|
11
|
+
:param n: Chunk size for resulting lists
|
|
12
|
+
:type: int
|
|
13
|
+
:return: iterator of the sliced list
|
|
14
|
+
|
|
15
|
+
Source: http://stackoverflow.com/questions/3992735/python-generator-that-groups-another-iterable-into-groups-of-n
|
|
16
|
+
"""
|
|
17
|
+
iterable = iter(iterable)
|
|
18
|
+
return iter(lambda: list(islice(iterable, n)), [])
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def getChunk(arr, step):
|
|
22
|
+
"""
|
|
23
|
+
Return chunk of entries from given array and step, it is similar in behavior to grouper
|
|
24
|
+
function but instead of returning new list it provides a generator iterable object.
|
|
25
|
+
:param arr: input array of data
|
|
26
|
+
:param step: step to iterate
|
|
27
|
+
:return: generator, set of slices with number of entries equal to step of iteration
|
|
28
|
+
"""
|
|
29
|
+
for i in range(0, len(arr), step):
|
|
30
|
+
yield arr[i:i + step]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def flattenList(doubleList):
|
|
34
|
+
"""
|
|
35
|
+
Make flat a list of lists.
|
|
36
|
+
"""
|
|
37
|
+
return list(chain.from_iterable(doubleList))
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def nestedDictUpdate(d, u):
|
|
41
|
+
"""
|
|
42
|
+
Code from Alex Matelli
|
|
43
|
+
http://stackoverflow.com/questions/3232943/update-value-of-a-nested-dictionary-of-varying-depth
|
|
44
|
+
"""
|
|
45
|
+
for k, v in u.items():
|
|
46
|
+
if isinstance(v, collections.abc.Mapping):
|
|
47
|
+
r = nestedDictUpdate(d.get(k, {}), v)
|
|
48
|
+
d[k] = r
|
|
49
|
+
else:
|
|
50
|
+
d[k] = u[k]
|
|
51
|
+
return d
|
|
52
|
+
|
|
53
|
+
def convertFromUnicodeToBytes(data):
|
|
54
|
+
"""
|
|
55
|
+
code fram
|
|
56
|
+
http://stackoverflow.com/questions/1254454/fastest-way-to-convert-a-dicts-keys-values-from-unicode-to-str
|
|
57
|
+
"""
|
|
58
|
+
if isinstance(data, str):
|
|
59
|
+
return data.encode('utf-8')
|
|
60
|
+
elif isinstance(data, collections.abc.Mapping):
|
|
61
|
+
return dict(list(map(convertFromUnicodeToBytes, list(data.items()))))
|
|
62
|
+
elif isinstance(data, collections.abc.Iterable):
|
|
63
|
+
return type(data)(list(map(convertFromUnicodeToBytes, data)))
|
|
64
|
+
else:
|
|
65
|
+
return data
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def makeListElementsUnique(listObj):
|
|
69
|
+
"""
|
|
70
|
+
Given a list of lists or a list of tuples, find all duplicate elements
|
|
71
|
+
and make them unique.
|
|
72
|
+
:param listObj: an unsorted list of lists or a list of tuples, e.g.:
|
|
73
|
+
[[1, 1], [1, 5], [1, 1]]; or
|
|
74
|
+
[(1, 1), (1, 5), (1, 1)]
|
|
75
|
+
:return: the same list object but with no duplicates
|
|
76
|
+
|
|
77
|
+
Source: https://stackoverflow.com/questions/2213923/removing-duplicates-from-a-list-of-lists
|
|
78
|
+
"""
|
|
79
|
+
listObj.sort()
|
|
80
|
+
return list(k for k, _ in groupby(listObj))
|
Utils/MathUtils.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
Module containing mathematical and physics utils
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
from builtins import int, str
|
|
8
|
+
from math import ceil
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def quantize(inputVal, quanta):
|
|
12
|
+
"""
|
|
13
|
+
_quantize_
|
|
14
|
+
|
|
15
|
+
Quantize the input value following the quanta provided.
|
|
16
|
+
"""
|
|
17
|
+
if isinstance(inputVal, str):
|
|
18
|
+
inputVal = float(inputVal)
|
|
19
|
+
elif not isinstance(inputVal, (int, float)):
|
|
20
|
+
msg = "Input value has to be either int or float, not %s" % (type(inputVal))
|
|
21
|
+
raise ValueError(msg)
|
|
22
|
+
|
|
23
|
+
if isinstance(quanta, (str, int, float)):
|
|
24
|
+
quanta = int(float(quanta))
|
|
25
|
+
elif not isinstance(quanta, int):
|
|
26
|
+
msg = "Quanta value has to be either int or float, not %s" % (type(quanta))
|
|
27
|
+
raise ValueError(msg)
|
|
28
|
+
|
|
29
|
+
res = int(ceil(inputVal / quanta))
|
|
30
|
+
|
|
31
|
+
return res * quanta
|
Utils/MemoryCache.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Simple in-memory and non-thread safe cache.
|
|
5
|
+
Note that this module does not support home-made object types, since there is
|
|
6
|
+
an explicit data type check when adding a new item to the cache.
|
|
7
|
+
|
|
8
|
+
It raises a TypeError exception if the cache data type chagens;
|
|
9
|
+
or if the user tries to extend the cache with an incompatible
|
|
10
|
+
data type.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from copy import copy
|
|
14
|
+
|
|
15
|
+
from builtins import object
|
|
16
|
+
from time import time
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MemoryCacheException(Exception):
|
|
20
|
+
def __init__(self, message):
|
|
21
|
+
super(MemoryCacheException, self).__init__(message)
|
|
22
|
+
|
|
23
|
+
class MemoryCache():
|
|
24
|
+
|
|
25
|
+
__slots__ = ["lastUpdate", "expiration", "_cache"]
|
|
26
|
+
|
|
27
|
+
def __init__(self, expiration, initialData=None):
|
|
28
|
+
"""
|
|
29
|
+
Initializes cache object
|
|
30
|
+
|
|
31
|
+
:param expiration: expiration time in seconds
|
|
32
|
+
:param initialData: initial value for the cache
|
|
33
|
+
"""
|
|
34
|
+
self.lastUpdate = int(time())
|
|
35
|
+
self.expiration = expiration
|
|
36
|
+
self._cache = initialData
|
|
37
|
+
|
|
38
|
+
def __contains__(self, item):
|
|
39
|
+
"""
|
|
40
|
+
Check whether item is in the current cache
|
|
41
|
+
:param item: a simple object (string, integer, etc)
|
|
42
|
+
:return: True if the object can be found in the cache, False otherwise
|
|
43
|
+
"""
|
|
44
|
+
return item in self._cache
|
|
45
|
+
|
|
46
|
+
def __getitem__(self, keyName):
|
|
47
|
+
"""
|
|
48
|
+
If the cache is a dictionary, return that item from the cache. Else, raise an exception.
|
|
49
|
+
:param keyName: the key name from the dictionary
|
|
50
|
+
"""
|
|
51
|
+
if isinstance(self._cache, dict):
|
|
52
|
+
return copy(self._cache.get(keyName))
|
|
53
|
+
else:
|
|
54
|
+
raise MemoryCacheException("Cannot retrieve an item from a non-dict MemoryCache object: {}".format(self._cache))
|
|
55
|
+
|
|
56
|
+
def reset(self):
|
|
57
|
+
"""
|
|
58
|
+
Resets the cache to its current data type
|
|
59
|
+
"""
|
|
60
|
+
if isinstance(self._cache, (dict, set)):
|
|
61
|
+
self._cache.clear()
|
|
62
|
+
elif isinstance(self._cache, list):
|
|
63
|
+
del self._cache[:]
|
|
64
|
+
else:
|
|
65
|
+
raise MemoryCacheException("The cache needs to be reset manually, data type unknown")
|
|
66
|
+
|
|
67
|
+
def isCacheExpired(self):
|
|
68
|
+
"""
|
|
69
|
+
Evaluate whether the cache has already expired, returning
|
|
70
|
+
True if it did, otherwise it returns False
|
|
71
|
+
"""
|
|
72
|
+
return self.lastUpdate + self.expiration < int(time())
|
|
73
|
+
|
|
74
|
+
def getCache(self):
|
|
75
|
+
"""
|
|
76
|
+
Raises an exception if the cache has expired, otherwise returns
|
|
77
|
+
its data
|
|
78
|
+
"""
|
|
79
|
+
if self.isCacheExpired():
|
|
80
|
+
expiredSince = int(time()) - (self.lastUpdate + self.expiration)
|
|
81
|
+
raise MemoryCacheException("Memory cache expired for %d seconds" % expiredSince)
|
|
82
|
+
return self._cache
|
|
83
|
+
|
|
84
|
+
def setCache(self, inputData):
|
|
85
|
+
"""
|
|
86
|
+
Refresh the cache with the content provided (refresh its expiration as well)
|
|
87
|
+
This method enforces the user to not change the cache data type
|
|
88
|
+
:param inputData: data to store in the cache
|
|
89
|
+
"""
|
|
90
|
+
if not isinstance(self._cache, type(inputData)):
|
|
91
|
+
raise TypeError("Current cache data type: %s, while new value is: %s" %
|
|
92
|
+
(type(self._cache), type(inputData)))
|
|
93
|
+
self.reset()
|
|
94
|
+
self.lastUpdate = int(time())
|
|
95
|
+
self._cache = inputData
|
|
96
|
+
|
|
97
|
+
def addItemToCache(self, inputItem):
|
|
98
|
+
"""
|
|
99
|
+
Adds new item(s) to the cache, without resetting its expiration.
|
|
100
|
+
It, of course, only works for data caches of type: list, set or dict.
|
|
101
|
+
:param inputItem: additional item to be added to the current cached data
|
|
102
|
+
"""
|
|
103
|
+
if isinstance(self._cache, set) and isinstance(inputItem, (list, set)):
|
|
104
|
+
# extend another list or set into a set
|
|
105
|
+
self._cache.update(inputItem)
|
|
106
|
+
elif isinstance(self._cache, set) and isinstance(inputItem, (int, float, str)):
|
|
107
|
+
# add a simple object (integer, string, etc) to a set
|
|
108
|
+
self._cache.add(inputItem)
|
|
109
|
+
elif isinstance(self._cache, list) and isinstance(inputItem, (list, set)):
|
|
110
|
+
# extend another list or set into a list
|
|
111
|
+
self._cache.extend(inputItem)
|
|
112
|
+
elif isinstance(self._cache, list) and isinstance(inputItem, (int, float, str)):
|
|
113
|
+
# add a simple object (integer, string, etc) to a list
|
|
114
|
+
self._cache.append(inputItem)
|
|
115
|
+
elif isinstance(self._cache, dict) and isinstance(inputItem, dict):
|
|
116
|
+
self._cache.update(inputItem)
|
|
117
|
+
else:
|
|
118
|
+
msg = "Input item type: %s cannot be added to a cache type: %s" % (type(self._cache), type(inputItem))
|
|
119
|
+
raise TypeError("Cache and input item data type mismatch. %s" % msg)
|
Utils/Patterns.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Patterns module provides set of CS patterns
|
|
3
|
+
"""
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
class Singleton(type):
|
|
7
|
+
"""Implementation of Singleton class"""
|
|
8
|
+
_instances = {}
|
|
9
|
+
def __call__(cls, *args, **kwargs):
|
|
10
|
+
if cls not in cls._instances:
|
|
11
|
+
cls._instances[cls] = \
|
|
12
|
+
super(Singleton, cls).__call__(*args, **kwargs)
|
|
13
|
+
return cls._instances[cls]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def getDomainName(urlStr):
|
|
17
|
+
"""
|
|
18
|
+
Given a URL string, return the domain name.
|
|
19
|
+
:param urlStr: URL string
|
|
20
|
+
:return: a string with the domain name (e.g. "cmsweb-prod")
|
|
21
|
+
"""
|
|
22
|
+
domainPattern = re.compile(r'https?://([^/]+)\.cern\.ch')
|
|
23
|
+
match = domainPattern.search(urlStr)
|
|
24
|
+
return match.group(1) if match else ""
|
Utils/Pipeline.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""
|
|
2
|
+
File : Pipeline.py
|
|
3
|
+
Description: Provides 2 basic classes:
|
|
4
|
+
- Functor: A class to create function calls from a function object
|
|
5
|
+
and arbitrary number of arguments
|
|
6
|
+
- Pipeline: A class to provide building blocks for creating functional
|
|
7
|
+
pipelines for cumulative execution on an arbitrary object
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from builtins import object
|
|
11
|
+
from functools import reduce
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Functor(object):
|
|
15
|
+
"""
|
|
16
|
+
A simple functor class used to construct a function call which later to be
|
|
17
|
+
applied on an (any type) object.
|
|
18
|
+
NOTE:
|
|
19
|
+
It expects a function in the constructor and an (any type) object
|
|
20
|
+
passed to the run or __call__ methods, which methods once called they
|
|
21
|
+
construct and return the following function:
|
|
22
|
+
func(obj, *args, **kwargs)
|
|
23
|
+
NOTE:
|
|
24
|
+
All the additional arguments which the function may take must be set in
|
|
25
|
+
the __init__ method. If any of them are passed during run time an error
|
|
26
|
+
will be raised.
|
|
27
|
+
|
|
28
|
+
:func:
|
|
29
|
+
The function to which the rest of the constructor arguments are about
|
|
30
|
+
to be attached and then the newly created function will be returned.
|
|
31
|
+
- The function needs to take at least one parameter since the object
|
|
32
|
+
passed to the run/__call__ methods will always be put as a first
|
|
33
|
+
argument to the function.
|
|
34
|
+
|
|
35
|
+
:Example:
|
|
36
|
+
|
|
37
|
+
def adder(a, b, *args, **kwargs):
|
|
38
|
+
if args:
|
|
39
|
+
print("adder args: %s" % args)
|
|
40
|
+
if kwargs:
|
|
41
|
+
print("adder kwargs: %s" % kwargs)
|
|
42
|
+
res = a + b
|
|
43
|
+
return res
|
|
44
|
+
|
|
45
|
+
>>> x=Functor(adder, 8, 'foo', bar=True)
|
|
46
|
+
>>> x(2)
|
|
47
|
+
adder args: foo
|
|
48
|
+
adder kwargs: {'bar': True}
|
|
49
|
+
adder res: 10
|
|
50
|
+
10
|
|
51
|
+
|
|
52
|
+
>>> x
|
|
53
|
+
<Pipeline.Functor instance at 0x7f319bbaeea8>
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
"""
|
|
57
|
+
def __init__(self, func, *args, **kwargs):
|
|
58
|
+
"""
|
|
59
|
+
The init method for class Functor
|
|
60
|
+
"""
|
|
61
|
+
self.func = func
|
|
62
|
+
self.args = args
|
|
63
|
+
self.kwargs = kwargs
|
|
64
|
+
|
|
65
|
+
def __call__(self, obj):
|
|
66
|
+
"""
|
|
67
|
+
The call method for class Functor
|
|
68
|
+
"""
|
|
69
|
+
return self.run(obj)
|
|
70
|
+
|
|
71
|
+
def run(self, obj):
|
|
72
|
+
return self.func(obj, *self.args, **self.kwargs)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class Pipeline(object):
|
|
76
|
+
"""
|
|
77
|
+
A simple Functional Pipeline Class: applies a set of functions to an object,
|
|
78
|
+
where the output of every previous function is an input to the next one.
|
|
79
|
+
"""
|
|
80
|
+
# NOTE:
|
|
81
|
+
# Similar and inspiring approaches but yet some different implementations
|
|
82
|
+
# are discussed in the following two links [1] & [2]. With a quite good
|
|
83
|
+
# explanation in [1], which helped a lot. All in all at the bottom always
|
|
84
|
+
# sits the reduce function.
|
|
85
|
+
# [1]
|
|
86
|
+
# https://softwarejourneyman.com/python-function-pipelines.html
|
|
87
|
+
# [2]
|
|
88
|
+
# https://gitlab.com/mc706/functional-pipeline
|
|
89
|
+
|
|
90
|
+
def __init__(self, funcLine=None, name=None):
|
|
91
|
+
"""
|
|
92
|
+
:funcLine: A list of functions or Functors of function + arguments (see
|
|
93
|
+
the Class definition above) that are to be applied sequentially
|
|
94
|
+
to the object.
|
|
95
|
+
- If any of the elements of 'funcLine' is a function, a direct
|
|
96
|
+
function call with the object as an argument is performed.
|
|
97
|
+
- If any of the elements of 'funcLine' is a Functor, then the
|
|
98
|
+
first argument of the Functor constructor is the function to
|
|
99
|
+
be evaluated and the object is passed as a first argument to
|
|
100
|
+
the function with all the rest of the arguments passed right
|
|
101
|
+
after it eg. the following Functor in the funcLine:
|
|
102
|
+
|
|
103
|
+
Functor(func, 'foo', bar=True)
|
|
104
|
+
|
|
105
|
+
will result in the following function call later when the
|
|
106
|
+
pipeline is executed:
|
|
107
|
+
|
|
108
|
+
func(obj, 'foo', bar=True)
|
|
109
|
+
|
|
110
|
+
:Example:
|
|
111
|
+
(using the adder function from above and an object of type int)
|
|
112
|
+
|
|
113
|
+
>>> pipe = Pipeline([Functor(adder, 5),
|
|
114
|
+
Functor(adder, 6),
|
|
115
|
+
Functor(adder, 7, "extraArg"),
|
|
116
|
+
Functor(adder, 8, update=True)])
|
|
117
|
+
|
|
118
|
+
>>> pipe.run(1)
|
|
119
|
+
adder res: 6
|
|
120
|
+
adder res: 12
|
|
121
|
+
adder args: extraArg
|
|
122
|
+
adder res: 19
|
|
123
|
+
adder kwargs: {'update': True}
|
|
124
|
+
adder res: 27
|
|
125
|
+
"""
|
|
126
|
+
self.funcLine = funcLine or []
|
|
127
|
+
self.name = name
|
|
128
|
+
|
|
129
|
+
def getPipelineName(self):
|
|
130
|
+
"""
|
|
131
|
+
__getPipelineName__
|
|
132
|
+
"""
|
|
133
|
+
name = self.name or "Unnamed Pipeline"
|
|
134
|
+
return name
|
|
135
|
+
|
|
136
|
+
def run(self, obj):
|
|
137
|
+
return reduce(lambda obj, functor: functor(obj), self.funcLine, obj)
|
Utils/PortForward.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
_PortForward_
|
|
4
|
+
|
|
5
|
+
A decorator for swapping ports in an url
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from builtins import str, bytes
|
|
9
|
+
|
|
10
|
+
def portForward(port):
|
|
11
|
+
"""
|
|
12
|
+
Decorator wrapper function for port forwarding of the REST calls of any
|
|
13
|
+
function to a given port.
|
|
14
|
+
|
|
15
|
+
Currently there are three constraints for applying this decorator.
|
|
16
|
+
1. The function to be decorated must be defined within a class and not being a static method.
|
|
17
|
+
The reason for that is because we need to be sure the function's signature will
|
|
18
|
+
always include the class instance as its first argument.
|
|
19
|
+
2. The url argument must be present as the second one in the positional argument list
|
|
20
|
+
of the decorated function (right after the class instance argument).
|
|
21
|
+
3. The url must follow the syntax specifications in RFC 1808:
|
|
22
|
+
https://tools.ietf.org/html/rfc1808.html
|
|
23
|
+
|
|
24
|
+
If all of the above constraints are fulfilled and the url is part of the
|
|
25
|
+
urlMangleList, then the url is parsed and the port is substituted with the
|
|
26
|
+
one provided as an argument to the decorator's wrapper function.
|
|
27
|
+
|
|
28
|
+
param port: The port to which the REST call should be forwarded.
|
|
29
|
+
"""
|
|
30
|
+
def portForwardDecorator(callFunc):
|
|
31
|
+
"""
|
|
32
|
+
The actual decorator
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def portMangle(callObj, url, *args, **kwargs):
|
|
36
|
+
"""
|
|
37
|
+
Function used to check if the url coming with the current argument list
|
|
38
|
+
is to be forwarded and if so change the port to the one provided as an
|
|
39
|
+
argument to the decorator wrapper.
|
|
40
|
+
|
|
41
|
+
:param classObj: This is the class object (slef from within the class)
|
|
42
|
+
which is always to be present in the signature of a
|
|
43
|
+
public method. We will never use this argument, but
|
|
44
|
+
we need it there for not breaking the positional
|
|
45
|
+
argument order
|
|
46
|
+
:param url: This is the actual url to be (eventually) forwarded
|
|
47
|
+
:param *args: The positional argument list coming from the original function
|
|
48
|
+
:param *kwargs: The keywords argument list coming from the original function
|
|
49
|
+
"""
|
|
50
|
+
forwarded = False
|
|
51
|
+
try:
|
|
52
|
+
if isinstance(url, str):
|
|
53
|
+
urlToMangle = 'https://cmsweb'
|
|
54
|
+
if url.startswith(urlToMangle):
|
|
55
|
+
newUrl = url.replace('.cern.ch/', '.cern.ch:%d/' % port, 1)
|
|
56
|
+
forwarded = True
|
|
57
|
+
elif isinstance(url, bytes):
|
|
58
|
+
urlToMangle = b'https://cmsweb'
|
|
59
|
+
if url.startswith(urlToMangle):
|
|
60
|
+
newUrl = url.replace(b'.cern.ch/', b'.cern.ch:%d/' % port, 1)
|
|
61
|
+
forwarded = True
|
|
62
|
+
|
|
63
|
+
except Exception:
|
|
64
|
+
pass
|
|
65
|
+
if forwarded:
|
|
66
|
+
return callFunc(callObj, newUrl, *args, **kwargs)
|
|
67
|
+
else:
|
|
68
|
+
return callFunc(callObj, url, *args, **kwargs)
|
|
69
|
+
return portMangle
|
|
70
|
+
return portForwardDecorator
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class PortForward():
|
|
74
|
+
"""
|
|
75
|
+
A class with a call method implementing a simple way to use the functionality
|
|
76
|
+
provided by the protForward decorator as a pure functional call:
|
|
77
|
+
EXAMPLE:
|
|
78
|
+
from Utils.PortForward import PortForward
|
|
79
|
+
|
|
80
|
+
portForwarder = PortForward(8443)
|
|
81
|
+
url = 'https://cmsweb-testbed.cern.ch/couchdb'
|
|
82
|
+
url = portForwarder(url)
|
|
83
|
+
"""
|
|
84
|
+
def __init__(self, port):
|
|
85
|
+
"""
|
|
86
|
+
The init method for the PortForward call class. This one is supposed
|
|
87
|
+
to simply provide an initial class instance with a logger.
|
|
88
|
+
"""
|
|
89
|
+
self.port = port
|
|
90
|
+
|
|
91
|
+
def __call__(self, url):
|
|
92
|
+
"""
|
|
93
|
+
The call method for the PortForward class
|
|
94
|
+
"""
|
|
95
|
+
def dummyCall(self, url):
|
|
96
|
+
return url
|
|
97
|
+
return portForward(self.port)(dummyCall)(self, url)
|
Utils/ProcFS.py
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
File : ProcFS.py
|
|
5
|
+
Author : Valentin Kuznetsov <vkuznet AT gmail dot com>
|
|
6
|
+
Description: This module provides function which rely on Linux proc fs
|
|
7
|
+
"""
|
|
8
|
+
# system modules
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
# 3rd party modules
|
|
12
|
+
try:
|
|
13
|
+
import psutil
|
|
14
|
+
usePsutil = True
|
|
15
|
+
except ImportError:
|
|
16
|
+
usePsutil = False
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def processStatus(pid, method=None):
|
|
20
|
+
"""
|
|
21
|
+
Return status of the given process PID along with its threads using psutil.
|
|
22
|
+
:param pid: Process ID to inspect
|
|
23
|
+
:param method: define which method to use procfs or psutil
|
|
24
|
+
:return: list of dictionaries of `{process, pid, status}` data-structure
|
|
25
|
+
"""
|
|
26
|
+
if method == 'procfs' or usePsutil == False:
|
|
27
|
+
return processStatusViaProcFS(pid)
|
|
28
|
+
return processStatusViaPsutil(pid)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def processStatusViaPsutil(pid):
|
|
32
|
+
"""
|
|
33
|
+
Return status of the given process PID along with its threads using psutil.
|
|
34
|
+
:param pid: Process ID to inspect
|
|
35
|
+
:return: list of dictionaries with {process, pid, status, type}
|
|
36
|
+
"""
|
|
37
|
+
statusList = []
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
proc = psutil.Process(int(pid))
|
|
41
|
+
processName = proc.name()
|
|
42
|
+
processStatus = proc.status()
|
|
43
|
+
statusList.append({
|
|
44
|
+
"process": processName,
|
|
45
|
+
"pid": str(pid),
|
|
46
|
+
"status": processStatus,
|
|
47
|
+
"type": "process"
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
# Iterate over threads
|
|
51
|
+
threads = proc.threads()
|
|
52
|
+
for thread in threads:
|
|
53
|
+
if str(pid) == str(thread.id):
|
|
54
|
+
continue
|
|
55
|
+
threadId = thread.id
|
|
56
|
+
# psutil doesn't provide thread name or detailed state, so we approximate
|
|
57
|
+
statusList.append({
|
|
58
|
+
"process": f"{processName}-thread",
|
|
59
|
+
"pid": str(threadId),
|
|
60
|
+
"status": "running", # threads are assumed running if present
|
|
61
|
+
"type": "thread"
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
except psutil.NoSuchProcess:
|
|
65
|
+
return [{"error": f"Process {pid} not found"}]
|
|
66
|
+
except Exception as e:
|
|
67
|
+
return [{"error": str(e)}]
|
|
68
|
+
|
|
69
|
+
return statusList
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def processStatusViaProcFS(pid):
|
|
73
|
+
"""
|
|
74
|
+
Return status of given process PID along with its threads via /proc FS look-up (available on all Linux OSes)
|
|
75
|
+
:return: list of dictionaries of `{process, pid, status}` data-structure
|
|
76
|
+
"""
|
|
77
|
+
statusList = []
|
|
78
|
+
procPath = f"/proc/{pid}"
|
|
79
|
+
|
|
80
|
+
if not os.path.exists(procPath):
|
|
81
|
+
return [{"error": f"Process {pid} not found"}]
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
with open(f"{procPath}/status", encoding='utf-8') as f:
|
|
85
|
+
processName = ""
|
|
86
|
+
processState = ""
|
|
87
|
+
for line in f:
|
|
88
|
+
if line.startswith("Name:"):
|
|
89
|
+
processName = line.split(":")[1].strip()
|
|
90
|
+
elif line.startswith("State:"):
|
|
91
|
+
processState = line.split(":")[1].strip()
|
|
92
|
+
statusList.append({"process": processName, "pid": str(pid), "status": processState, "type": "process"})
|
|
93
|
+
|
|
94
|
+
taskPath = f"{procPath}/task"
|
|
95
|
+
if os.path.exists(taskPath):
|
|
96
|
+
for threadId in os.listdir(taskPath):
|
|
97
|
+
if str(threadId) == str(pid):
|
|
98
|
+
continue
|
|
99
|
+
with open(f"{taskPath}/{threadId}/status", encoding='utf-8') as f:
|
|
100
|
+
threadName = ""
|
|
101
|
+
threadState = ""
|
|
102
|
+
for line in f:
|
|
103
|
+
if line.startswith("Name:"):
|
|
104
|
+
threadName = line.split(":")[1].strip()
|
|
105
|
+
elif line.startswith("State:"):
|
|
106
|
+
threadState = line.split(":")[1].strip()
|
|
107
|
+
statusList.append({"process": threadName, "pid": threadId, "status": threadState, "type": "thread"})
|
|
108
|
+
|
|
109
|
+
except Exception as e:
|
|
110
|
+
return [{"error": str(e)}]
|
|
111
|
+
|
|
112
|
+
return statusList
|