wmglobalqueue 2.3.10__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.
Potentially problematic release.
This version of wmglobalqueue might be problematic. Click here for more details.
- Utils/CPMetrics.py +270 -0
- Utils/CertTools.py +62 -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/ProcessStats.py +103 -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 +308 -0
- Utils/__init__.py +11 -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 +651 -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 +1349 -0
- WMCore/Database/ConfigDBMap.py +29 -0
- WMCore/Database/CouchUtils.py +118 -0
- WMCore/Database/DBCore.py +198 -0
- WMCore/Database/DBCreator.py +113 -0
- WMCore/Database/DBExceptionHandler.py +57 -0
- WMCore/Database/DBFactory.py +110 -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 +623 -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 +113 -0
- WMCore/Services/DBS/DBSReader.py +23 -0
- WMCore/Services/DBS/DBSUtils.py +139 -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/PyCondorUtils.py +105 -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 +1287 -0
- WMCore/Services/Rucio/RucioUtils.py +74 -0
- WMCore/Services/Rucio/__init__.py +0 -0
- WMCore/Services/RucioConMon/RucioConMon.py +128 -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 +228 -0
- WMCore/WMLogging.py +108 -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 +1980 -0
- WMCore/WMSpec/WMWorkload.py +2288 -0
- WMCore/WMSpec/WMWorkloadTools.py +370 -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.3.10.data/data/bin/wmc-dist-patch +15 -0
- wmglobalqueue-2.3.10.data/data/bin/wmc-dist-unpatch +8 -0
- wmglobalqueue-2.3.10.data/data/bin/wmc-httpd +3 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/.couchapprc +1 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/README.md +40 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/_attachments/index.html +264 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/_attachments/js/ElementInfoByWorkflow.js +96 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/_attachments/js/StuckElementInfo.js +57 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/_attachments/js/WorkloadInfoTable.js +80 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/_attachments/js/dataTable.js +70 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/_attachments/js/namespace.js +23 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/_attachments/style/main.css +75 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/couchapp.json +4 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/filters/childQueueFilter.js +13 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/filters/filterDeletedDocs.js +3 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/filters/queueFilter.js +11 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/language +1 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/lib/mustache.js +333 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/lib/validate.js +27 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/lib/workqueue_utils.js +61 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/lists/elementsDetail.js +28 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/lists/filter.js +86 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/lists/stuckElements.js +38 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/lists/workRestrictions.js +153 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/lists/workflowSummary.js +28 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/rewrites.json +73 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/shows/redirect.js +23 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/shows/status.js +40 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/templates/ElementSummaryByWorkflow.html +27 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/templates/StuckElementSummary.html +26 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/templates/TaskStatus.html +23 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/templates/WorkflowSummary.html +27 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/templates/partials/workqueue-common-lib.html +2 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/templates/partials/yui-lib-remote.html +16 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/templates/partials/yui-lib.html +18 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/updates/in-place.js +50 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/validate_doc_update.js +8 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/vendor/couchapp/_attachments/jquery.couch.app.js +235 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/vendor/couchapp/_attachments/jquery.pathbinder.js +173 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/activeData/map.js +8 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/activeData/reduce.js +2 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/activeParentData/map.js +8 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/activeParentData/reduce.js +2 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/activePileupData/map.js +8 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/activePileupData/reduce.js +2 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/analyticsData/map.js +11 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/analyticsData/reduce.js +1 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/availableByPriority/map.js +6 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/conflicts/map.js +5 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/elements/map.js +5 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/elementsByData/map.js +8 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/elementsByParent/map.js +8 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/elementsByParentData/map.js +8 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/elementsByPileupData/map.js +8 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/elementsByStatus/map.js +8 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/elementsBySubscription/map.js +6 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/elementsByWorkflow/map.js +8 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/elementsByWorkflow/reduce.js +3 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/elementsDetailByWorkflowAndStatus/map.js +26 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/jobInjectStatusByRequest/map.js +10 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/jobInjectStatusByRequest/reduce.js +1 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/jobStatusByRequest/map.js +6 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/jobStatusByRequest/reduce.js +1 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/jobsByChildQueueAndPriority/map.js +6 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/jobsByChildQueueAndPriority/reduce.js +1 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/jobsByChildQueueAndStatus/map.js +6 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/jobsByChildQueueAndStatus/reduce.js +1 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/jobsByRequest/map.js +6 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/jobsByRequest/reduce.js +1 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/jobsByStatus/map.js +6 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/jobsByStatus/reduce.js +1 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/jobsByStatusAndPriority/map.js +6 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/jobsByStatusAndPriority/reduce.js +1 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/openRequests/map.js +6 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/recent-items/map.js +5 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/siteWhitelistByRequest/map.js +6 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/siteWhitelistByRequest/reduce.js +1 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/specsByWorkflow/map.js +5 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/stuckElements/map.js +38 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/wmbsInjectStatusByRequest/map.js +12 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/wmbsInjectStatusByRequest/reduce.js +3 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/wmbsUrl/map.js +6 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/wmbsUrl/reduce.js +2 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/wmbsUrlByRequest/map.js +6 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/wmbsUrlByRequest/reduce.js +2 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/workflowSummary/map.js +9 -0
- wmglobalqueue-2.3.10.data/data/data/couchapps/WorkQueue/views/workflowSummary/reduce.js +10 -0
- wmglobalqueue-2.3.10.dist-info/LICENSE +202 -0
- wmglobalqueue-2.3.10.dist-info/METADATA +24 -0
- wmglobalqueue-2.3.10.dist-info/NOTICE +16 -0
- wmglobalqueue-2.3.10.dist-info/RECORD +345 -0
- wmglobalqueue-2.3.10.dist-info/WHEEL +5 -0
- wmglobalqueue-2.3.10.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
LogDBBackend
|
|
4
|
+
|
|
5
|
+
Interface to LogDB persistent storage
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from builtins import object, bytes
|
|
9
|
+
|
|
10
|
+
from Utils.PythonVersion import PY3
|
|
11
|
+
from Utils.Utilities import encodeUnicodeToBytes, encodeUnicodeToBytesConditional
|
|
12
|
+
|
|
13
|
+
# system modules
|
|
14
|
+
import datetime
|
|
15
|
+
import hashlib
|
|
16
|
+
import time
|
|
17
|
+
|
|
18
|
+
# WMCore modules
|
|
19
|
+
from WMCore.Database.CMSCouch import CouchServer, CouchNotFoundError
|
|
20
|
+
from WMCore.Services.LogDB.LogDBExceptions import LogDBError
|
|
21
|
+
|
|
22
|
+
# define full list of supported LogDB types
|
|
23
|
+
LOGDB_MSG_TYPES = ['info', 'error', 'warning', 'comment']
|
|
24
|
+
|
|
25
|
+
def gen_hash(key):
|
|
26
|
+
"Generate hash for given key"
|
|
27
|
+
key = encodeUnicodeToBytes(key) # if key is not unicode, then it is not changed
|
|
28
|
+
if not isinstance(key, bytes):
|
|
29
|
+
raise NotImplementedError
|
|
30
|
+
keyhash = hashlib.md5()
|
|
31
|
+
keyhash.update(encodeUnicodeToBytesConditional(key, condition=PY3))
|
|
32
|
+
return keyhash.hexdigest()
|
|
33
|
+
|
|
34
|
+
def tstamp():
|
|
35
|
+
"Return timestamp with microseconds"
|
|
36
|
+
now = datetime.datetime.now()
|
|
37
|
+
base = str(time.mktime(now.timetuple())).split('.')[0]
|
|
38
|
+
ctime = '%s.%s' % (base, now.microsecond)
|
|
39
|
+
return float(ctime)
|
|
40
|
+
|
|
41
|
+
def clean_entry(doc):
|
|
42
|
+
"""Clean document from CouchDB attributes"""
|
|
43
|
+
for attr in ['_rev', '_id']:
|
|
44
|
+
if attr in doc:
|
|
45
|
+
del doc[attr]
|
|
46
|
+
return doc
|
|
47
|
+
|
|
48
|
+
class LogDBBackend(object):
|
|
49
|
+
"""
|
|
50
|
+
Represents persistent storage for LogDB
|
|
51
|
+
"""
|
|
52
|
+
def __init__(self, db_url, db_name, identifier, thread_name, **kwds):
|
|
53
|
+
self.db_url = db_url
|
|
54
|
+
self.server = CouchServer(db_url)
|
|
55
|
+
self.db_name = db_name
|
|
56
|
+
self.dbid = identifier
|
|
57
|
+
self.thread_name = thread_name
|
|
58
|
+
self.agent = kwds.get('agent', 0)
|
|
59
|
+
self.db = self.server.connectDatabase(db_name, create=False)
|
|
60
|
+
self.design = 'LogDB' # name of design document
|
|
61
|
+
self.view = 'requests' # name of view to look-up requests
|
|
62
|
+
self.tsview = 'tstamp' # name of tsview to look-up requests
|
|
63
|
+
self.threadview = 'logByRequestAndThread'
|
|
64
|
+
self.requestview = 'logByRequest'
|
|
65
|
+
|
|
66
|
+
def deleteDatabase(self):
|
|
67
|
+
"""Delete back-end database"""
|
|
68
|
+
if self.db_name in self.server.listDatabases():
|
|
69
|
+
self.server.deleteDatabase(self.db_name)
|
|
70
|
+
|
|
71
|
+
def check(self, request, mtype=None):
|
|
72
|
+
"""Check that given request name is valid"""
|
|
73
|
+
# TODO: we may add some logic to check request name, etc.
|
|
74
|
+
if not request:
|
|
75
|
+
raise LogDBError("Request name is empty")
|
|
76
|
+
if mtype and mtype not in LOGDB_MSG_TYPES:
|
|
77
|
+
raise LogDBError("Unsupported message type: '%s', supported types %s" \
|
|
78
|
+
% (mtype, LOGDB_MSG_TYPES))
|
|
79
|
+
|
|
80
|
+
def docid(self, request, mtype):
|
|
81
|
+
"""Generate doc id, we use double dash to avoid dashes from thread names"""
|
|
82
|
+
return gen_hash('--'.join((request, self.dbid, self.thread_name, mtype)))
|
|
83
|
+
|
|
84
|
+
def prefix(self, mtype):
|
|
85
|
+
"""Generate agent specific prefix for given message type"""
|
|
86
|
+
if self.agent:
|
|
87
|
+
# we add prefix for agent messages, all others will not have this index
|
|
88
|
+
mtype = 'agent-%s' % mtype
|
|
89
|
+
return mtype
|
|
90
|
+
|
|
91
|
+
def agent_update(self, request, msg='', mtype="info"):
|
|
92
|
+
"""Update agent info in LogDB for given request"""
|
|
93
|
+
self.check(request, mtype)
|
|
94
|
+
mtype = self.prefix(mtype)
|
|
95
|
+
rec = {"ts":tstamp(), "msg":msg}
|
|
96
|
+
doc = {"_id": self.docid(request, mtype), "messages": [rec],
|
|
97
|
+
"request":request, "identifier":self.dbid,
|
|
98
|
+
"thr":self.thread_name, "type":mtype}
|
|
99
|
+
try:
|
|
100
|
+
exist_doc = self.db.document(doc["_id"])
|
|
101
|
+
doc["_rev"] = exist_doc["_rev"]
|
|
102
|
+
except CouchNotFoundError:
|
|
103
|
+
# this means document is not exist so we will just insert
|
|
104
|
+
pass
|
|
105
|
+
finally:
|
|
106
|
+
res = self.db.commitOne(doc)
|
|
107
|
+
return res
|
|
108
|
+
|
|
109
|
+
def user_update(self, request, msg, mtype='comment'):
|
|
110
|
+
"""Update user info in LogDB for given request"""
|
|
111
|
+
rec = {"ts":tstamp(), "msg":msg}
|
|
112
|
+
doc = {"_id": self.docid(request, mtype), "messages": [rec],
|
|
113
|
+
"request":request, "identifier":self.dbid,
|
|
114
|
+
"thr":self.thread_name, "type":mtype}
|
|
115
|
+
try:
|
|
116
|
+
exist_doc = self.db.document(doc["_id"])
|
|
117
|
+
doc["_rev"] = exist_doc["_rev"]
|
|
118
|
+
doc["messages"] += exist_doc["messages"]
|
|
119
|
+
except CouchNotFoundError:
|
|
120
|
+
# this means document is not exist so we will just insert
|
|
121
|
+
pass
|
|
122
|
+
finally:
|
|
123
|
+
res = self.db.commitOne(doc)
|
|
124
|
+
return res
|
|
125
|
+
|
|
126
|
+
def get(self, request, mtype=None, detail=True, agent=True):
|
|
127
|
+
"""Retrieve all entries from LogDB for given request"""
|
|
128
|
+
self.check(request, mtype)
|
|
129
|
+
if agent and mtype:
|
|
130
|
+
mtype = self.prefix(mtype)
|
|
131
|
+
options = {'reduce':False}
|
|
132
|
+
if mtype:
|
|
133
|
+
keys = [[request, mtype]]
|
|
134
|
+
else:
|
|
135
|
+
keys=[]
|
|
136
|
+
options.update({'startkey': [request], 'endkey':[request, {}]})
|
|
137
|
+
if detail:
|
|
138
|
+
options.update({'include_docs': True})
|
|
139
|
+
docs = self.db.loadView(self.design, self.view, options, keys=keys)
|
|
140
|
+
return docs
|
|
141
|
+
|
|
142
|
+
def get_by_thread(self, request, mtype='error', detail=False, agent=True):
|
|
143
|
+
self.check(request, mtype)
|
|
144
|
+
if agent and mtype:
|
|
145
|
+
mtype = self.prefix(mtype)
|
|
146
|
+
keys = [[request, self.dbid, self.thread_name, mtype]]
|
|
147
|
+
options = {'reduce':False}
|
|
148
|
+
if detail:
|
|
149
|
+
options.update({'include_docs': True})
|
|
150
|
+
docs = self.db.loadView(self.design, self.threadview, options, keys)
|
|
151
|
+
return docs
|
|
152
|
+
|
|
153
|
+
def get_by_request(self, request):
|
|
154
|
+
keys = [request]
|
|
155
|
+
options = {'reduce':False}
|
|
156
|
+
docs = self.db.loadView(self.design, self.requestview, options, keys)
|
|
157
|
+
return docs
|
|
158
|
+
|
|
159
|
+
def get_all_requests(self):
|
|
160
|
+
"""Retrieve all entries from LogDB"""
|
|
161
|
+
options = {'reduce':True, 'group_level':1}
|
|
162
|
+
docs = self.db.loadView(self.design, self.view, options)
|
|
163
|
+
return docs
|
|
164
|
+
|
|
165
|
+
def delete(self, request, mtype=None, this_thread=False, agent=True):
|
|
166
|
+
"""Delete entry in LogDB for given request"""
|
|
167
|
+
if mtype:
|
|
168
|
+
self.check(request, mtype)
|
|
169
|
+
else:
|
|
170
|
+
self.check(request)
|
|
171
|
+
if this_thread:
|
|
172
|
+
docs = self.get_by_thread(request, mtype=mtype, detail=False, agent=agent)
|
|
173
|
+
else:
|
|
174
|
+
docs = self.get(request, mtype=mtype, detail=False, agent=agent)
|
|
175
|
+
ids = [r['id'] for r in docs.get('rows', [])]
|
|
176
|
+
res = self.db.bulkDeleteByIDs(ids)
|
|
177
|
+
return res
|
|
178
|
+
|
|
179
|
+
def cleanup(self, thr):
|
|
180
|
+
"""
|
|
181
|
+
Clean-up docs older then given threshold (thr should be specified in seconds).
|
|
182
|
+
This is done via tstamp view end endkey, e.g.
|
|
183
|
+
curl "http://127.0.0.1:5984/logdb/_design/LogDB/_view/tstamp?endkey=1427912282"
|
|
184
|
+
"""
|
|
185
|
+
cutoff = int(round(time.time()-thr))
|
|
186
|
+
#docs = self.db.allDocs() # may need another view to look-up old docs
|
|
187
|
+
spec = {'endkey':cutoff, 'reduce':False}
|
|
188
|
+
docs = self.db.loadView(self.design, self.tsview, spec)
|
|
189
|
+
ids = [d['id'] for d in docs.get('rows', [])]
|
|
190
|
+
self.db.bulkDeleteByIDs(ids)
|
|
191
|
+
return ids
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""LogDB Exceptions"""
|
|
2
|
+
|
|
3
|
+
class LogDBError(Exception):
|
|
4
|
+
"""Standard error baseclass"""
|
|
5
|
+
def __init__(self, error):
|
|
6
|
+
Exception.__init__(self, error)
|
|
7
|
+
self.msg = LogDBError.__class__.__name__
|
|
8
|
+
self.error = error
|
|
9
|
+
|
|
10
|
+
def __str__(self):
|
|
11
|
+
return "%s: %s" % (self.msg, self.error)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
#-*- coding: utf-8 -*-
|
|
3
|
+
#pylint: disable=
|
|
4
|
+
"""
|
|
5
|
+
File : LogDBReport.py
|
|
6
|
+
Author : Valentin Kuznetsov <vkuznet AT gmail dot com>
|
|
7
|
+
Description: LogDB report class to represent LogDB messages
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import print_function
|
|
10
|
+
|
|
11
|
+
from builtins import range
|
|
12
|
+
from builtins import object
|
|
13
|
+
|
|
14
|
+
class LogDBReport(object):
|
|
15
|
+
"""LogDBReport class to represent LogDB messages"""
|
|
16
|
+
def __init__(self, logdb):
|
|
17
|
+
self.logdb = logdb
|
|
18
|
+
|
|
19
|
+
def docs(self, request):
|
|
20
|
+
"""Fetch LogDB messages for given request"""
|
|
21
|
+
if request == 'all':
|
|
22
|
+
docs = self.logdb.get_all_requests()
|
|
23
|
+
else:
|
|
24
|
+
docs = self.logdb.get(request)
|
|
25
|
+
return docs
|
|
26
|
+
|
|
27
|
+
def orderby(self, docs, order):
|
|
28
|
+
"""Order LogDB messages by given type"""
|
|
29
|
+
odict = {}
|
|
30
|
+
for item in docs:
|
|
31
|
+
odict[item['ts']] = item
|
|
32
|
+
keys = sorted(odict.keys())
|
|
33
|
+
keys.reverse()
|
|
34
|
+
out = []
|
|
35
|
+
for key in keys:
|
|
36
|
+
out.append(odict[key])
|
|
37
|
+
return out
|
|
38
|
+
|
|
39
|
+
def to_json(self, request, order='ts'):
|
|
40
|
+
"""Represent given messages in JSON data-format for given set of requests"""
|
|
41
|
+
docs = self.orderby(self.docs(request), order)
|
|
42
|
+
return docs
|
|
43
|
+
|
|
44
|
+
def to_txt(self, request, order='ts', sep=' '):
|
|
45
|
+
"""Represent given messages in ASCII text format for given set of requests"""
|
|
46
|
+
docs = self.orderby(self.docs(request), order)
|
|
47
|
+
keys = list(docs[0])
|
|
48
|
+
out = sep.join(keys) + '\n'
|
|
49
|
+
for doc in docs:
|
|
50
|
+
values = []
|
|
51
|
+
for key in keys:
|
|
52
|
+
values.append('%s' % doc[key])
|
|
53
|
+
out += sep.join(values) + '\n'
|
|
54
|
+
return out
|
|
55
|
+
|
|
56
|
+
def to_html(self, request, order='ts'):
|
|
57
|
+
"""Represent given messages in ASCII text format for given set of requests"""
|
|
58
|
+
out = '<table id="logdb-report">\n'
|
|
59
|
+
for doc in self.to_txt(request, order, sep='</td><td>').split('\n'):
|
|
60
|
+
if doc:
|
|
61
|
+
out += '<tr><td>'+doc+'</td></tr>\n'
|
|
62
|
+
out += '</table>'
|
|
63
|
+
return out
|
|
64
|
+
|
|
65
|
+
def to_stdout(self, request, order='ts'):
|
|
66
|
+
"""Yield to stdout LogDB messages for given request/type"""
|
|
67
|
+
msg = '\nReport for %s' % request
|
|
68
|
+
print(msg, '\n', '-'*len(msg))
|
|
69
|
+
docs = self.orderby(self.docs(request), order)
|
|
70
|
+
times = []
|
|
71
|
+
messages = []
|
|
72
|
+
mtypes = []
|
|
73
|
+
for doc in docs:
|
|
74
|
+
times.append(str(doc['ts']))
|
|
75
|
+
messages.append(doc['msg'])
|
|
76
|
+
mtypes.append(doc['type'])
|
|
77
|
+
tstpad = max([len(t) for t in times])
|
|
78
|
+
msgpad = max([len(m) for m in messages])
|
|
79
|
+
mtppad = max([len(m) for m in mtypes])
|
|
80
|
+
out = []
|
|
81
|
+
for idx in range(len(times)):
|
|
82
|
+
tcol = '%s%s' % (times[idx], ' '*(tstpad-len(times[idx])))
|
|
83
|
+
mcol = '%s%s' % (messages[idx], ' '*(msgpad-len(messages[idx])))
|
|
84
|
+
ecol = '%s%s' % (mtypes[idx], ' '*(mtppad-len(mtypes[idx])))
|
|
85
|
+
print("%s %s %s" % (tcol, mcol, ecol))
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains a few basic WMCore-related utilitarian functions
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from Utils.CertTools import ckey, cert
|
|
6
|
+
|
|
7
|
+
from WMCore.Services.pycurl_manager import RequestHandler
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def makeHttpRequest(srvUrl, payload=None, method='GET', headers=None, encode=True, decode=True, payloadSizeToDump=50):
|
|
11
|
+
"""
|
|
12
|
+
Perform HTTP call to provided service URL. This can be any of supported methods like
|
|
13
|
+
GET, POST, PUT, DELETE. It will return HTTP response from upstream MicroService
|
|
14
|
+
and parse it accordingly (the WMCore MS service returns results as {'result': {'data':...}}
|
|
15
|
+
HTTP response.
|
|
16
|
+
|
|
17
|
+
:param srvUrl: string with the service url
|
|
18
|
+
:param payload: payload query or document, in case of GET HTTP request it is query dictionary,
|
|
19
|
+
in case of POST/PUT HTTP requests it is JSON payload to the service
|
|
20
|
+
:param method: string, defines which HTTP method to use
|
|
21
|
+
:param headers: HTTP headers (presented as dictionarY)
|
|
22
|
+
:param encode: boolean to reflect if data should be encoded, see pycurl_manager.py
|
|
23
|
+
:param decode: boolean to reflect if data should be decoded, see pycurl_manager.py
|
|
24
|
+
:param payloadSizeToDump: amount of characters to dump in a log from payload (to make log entries readable)
|
|
25
|
+
:return: returns a list with all objects, or raises
|
|
26
|
+
an exception in case of failure
|
|
27
|
+
"""
|
|
28
|
+
payload = payload or {}
|
|
29
|
+
mgr = RequestHandler()
|
|
30
|
+
headers = headers or {'Content-Type': 'application/json'}
|
|
31
|
+
data = mgr.getdata(srvUrl, payload, headers, verb=method,
|
|
32
|
+
ckey=ckey(), cert=cert(), encode=encode, decode=decode)
|
|
33
|
+
if data and data.get("result", []):
|
|
34
|
+
if "error" in data["result"][0]:
|
|
35
|
+
# strip off part of payload to make readable log message
|
|
36
|
+
sdata = f"{payload}"
|
|
37
|
+
if len(sdata) > payloadSizeToDump:
|
|
38
|
+
sdata = sdata[:payloadSizeToDump] + "..."
|
|
39
|
+
msg = f"Failed to contact {srvUrl} via {method} request with {sdata}"
|
|
40
|
+
msg += f" and error message: {data}"
|
|
41
|
+
raise RuntimeError(msg)
|
|
42
|
+
return data["result"]
|
|
43
|
+
|
|
44
|
+
def getPileupDocs(mspileupUrl, queryDict=None, method='GET'):
|
|
45
|
+
"""
|
|
46
|
+
Fetch documents from MSPileup according to the query passed in using POST.
|
|
47
|
+
|
|
48
|
+
:param mspileupUrl: string with the MSPileup url
|
|
49
|
+
:param queryDict: dictionary with the MongoDB query to run
|
|
50
|
+
:param method: string, defines which HTTP method to use
|
|
51
|
+
:return: returns a list with all the pileup objects, or raises
|
|
52
|
+
an exception in case of failure
|
|
53
|
+
"""
|
|
54
|
+
return makeHttpRequest(mspileupUrl, payload=queryDict, method=method)
|
|
File without changes
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#! /bin/env python
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
A service class for retrieving data from McM using
|
|
5
|
+
an SSO cookie since it sits behind CERN SSO
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from builtins import str, object
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import pycurl
|
|
12
|
+
import subprocess
|
|
13
|
+
import logging
|
|
14
|
+
from typing import List
|
|
15
|
+
from io import BytesIO
|
|
16
|
+
from WMCore.WMException import WMException
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class McMNoDataError(WMException):
|
|
20
|
+
"""
|
|
21
|
+
_McMNoDataError_
|
|
22
|
+
McM responded but has no data for the request
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self):
|
|
26
|
+
WMException.__init__(self, 'McM responded correctly but has no data')
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class McM(object):
|
|
30
|
+
"""
|
|
31
|
+
A service class for retrieving data from McM using
|
|
32
|
+
an SSO cookie since it sits behind CERN SSO
|
|
33
|
+
'key' must be unencrypted
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, url='https://cms-pdmv.cern.ch/mcm', tmpDir='/tmp'):
|
|
37
|
+
self.url = url
|
|
38
|
+
self.tmpDir = tmpDir
|
|
39
|
+
self.logger = self._get_logger()
|
|
40
|
+
self.cookieFile = None
|
|
41
|
+
|
|
42
|
+
def __enter__(self):
|
|
43
|
+
self._krb_ticket()
|
|
44
|
+
self._get_cookie()
|
|
45
|
+
return self
|
|
46
|
+
|
|
47
|
+
def __exit__(self, exception_type, exception_value, traceback):
|
|
48
|
+
if self.cookieFile:
|
|
49
|
+
try:
|
|
50
|
+
os.remove(self.cookieFile)
|
|
51
|
+
except OSError:
|
|
52
|
+
return
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
def _get_logger(self) -> logging.Logger:
|
|
56
|
+
"""
|
|
57
|
+
Create a logger for McM client
|
|
58
|
+
"""
|
|
59
|
+
logger: logging.Logger = logging.getLogger("mcm_client")
|
|
60
|
+
date_format: str = "%Y-%m-%d %H:%M:%S %z"
|
|
61
|
+
format: str = "[%(levelname)s][%(name)s][%(asctime)s]: %(message)s"
|
|
62
|
+
formatter: logging.Formatter = logging.Formatter(fmt=format, datefmt=date_format)
|
|
63
|
+
handler: logging.StreamHandler = logging.StreamHandler()
|
|
64
|
+
|
|
65
|
+
handler.setLevel(logging.INFO)
|
|
66
|
+
handler.setFormatter(formatter)
|
|
67
|
+
logger.addHandler(handler)
|
|
68
|
+
return logger
|
|
69
|
+
|
|
70
|
+
def _krb_ticket(self) -> None:
|
|
71
|
+
"""
|
|
72
|
+
Check there is a valid Kerberos ticket for requesting a SSO
|
|
73
|
+
cookie. Raise a RuntimeError if there is not one.
|
|
74
|
+
"""
|
|
75
|
+
process = subprocess.Popen(["klist", "-f"],
|
|
76
|
+
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False)
|
|
77
|
+
stdout: str = process.communicate()[0].decode("utf-8")
|
|
78
|
+
if process.returncode != 0:
|
|
79
|
+
msg: str = ("There is no valid Kerberos ticket for requesting a SSO cookie. "
|
|
80
|
+
"Please make sure to provide one for your runtime environment"
|
|
81
|
+
)
|
|
82
|
+
self.logger.error(msg)
|
|
83
|
+
raise RuntimeError("FATAL -- %s\nError msg: %s" % (msg, stdout))
|
|
84
|
+
|
|
85
|
+
def _get_cookie(self) -> None:
|
|
86
|
+
"""
|
|
87
|
+
Request a SSO cookie to authenticate to McM.
|
|
88
|
+
"""
|
|
89
|
+
self.cookieFile = os.path.join(self.tmpDir, 'ssoCookie.txt')
|
|
90
|
+
command: List[str] = ["auth-get-sso-cookie", "-u", self.url, "-o", self.cookieFile, "-vv"]
|
|
91
|
+
callback_not_invoked: str = "DEBUG: Not automatically redirected: trying SAML authentication"
|
|
92
|
+
cookie_stored: str = "INFO: Saving cookies"
|
|
93
|
+
|
|
94
|
+
process = subprocess.Popen(command, stdout=subprocess.PIPE,
|
|
95
|
+
stderr=subprocess.STDOUT, shell=False)
|
|
96
|
+
stdout: str = process.communicate()[0].decode("utf-8")
|
|
97
|
+
stdout_list: List[str] = stdout.strip().split("\n")
|
|
98
|
+
if process.returncode != 0:
|
|
99
|
+
raise RuntimeError("FATAL -- Error requesting SSO cookie\nError msg: %s" % (stdout))
|
|
100
|
+
|
|
101
|
+
if callback_not_invoked in stdout:
|
|
102
|
+
callback_idx: int = stdout_list.index(callback_not_invoked)
|
|
103
|
+
stored_after_issue: bool = cookie_stored in stdout_list[callback_idx + 1]
|
|
104
|
+
if stored_after_issue:
|
|
105
|
+
msg: str = ("Callback method was not invoked. "
|
|
106
|
+
"Please make sure the provided Kerberos ticket is not linked to an account with 2FA"
|
|
107
|
+
)
|
|
108
|
+
raise RuntimeError(msg)
|
|
109
|
+
|
|
110
|
+
def _getURL(self, extendURL):
|
|
111
|
+
"""
|
|
112
|
+
Fetch an MCM URL with CURL using the SSO cookie.
|
|
113
|
+
Only intended to be used internally
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
b = BytesIO()
|
|
118
|
+
c = pycurl.Curl()
|
|
119
|
+
|
|
120
|
+
fullUrl = '%s/%s' % (self.url, extendURL)
|
|
121
|
+
|
|
122
|
+
c.setopt(c.URL, fullUrl)
|
|
123
|
+
c.setopt(c.SSL_VERIFYPEER, False)
|
|
124
|
+
c.setopt(c.SSL_VERIFYHOST, False)
|
|
125
|
+
c.setopt(c.FOLLOWLOCATION, True)
|
|
126
|
+
c.setopt(c.COOKIEJAR, self.cookieFile)
|
|
127
|
+
c.setopt(c.COOKIEFILE, self.cookieFile)
|
|
128
|
+
c.setopt(c.WRITEFUNCTION, b.write)
|
|
129
|
+
c.perform()
|
|
130
|
+
if c.getinfo(pycurl.HTTP_CODE) != 200:
|
|
131
|
+
raise IOError
|
|
132
|
+
except:
|
|
133
|
+
c.close()
|
|
134
|
+
raise IOError('Was not able to fetch or decode URL from McM')
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
body = b.getvalue()
|
|
138
|
+
res = json.loads(body)
|
|
139
|
+
except ValueError:
|
|
140
|
+
c.close()
|
|
141
|
+
raise IOError('Was not able to decode JSON from McM')
|
|
142
|
+
|
|
143
|
+
c.close()
|
|
144
|
+
return res
|
|
145
|
+
|
|
146
|
+
def getHistory(self, prepID):
|
|
147
|
+
"""
|
|
148
|
+
Get the history record which has who did what to an McM request
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
url = 'search?db_name=batches&contains=%s&get_raw' % prepID
|
|
153
|
+
res = self._getURL(url)
|
|
154
|
+
history = res.get("results", [])[0].get("history", [])
|
|
155
|
+
return history
|
|
156
|
+
except IndexError:
|
|
157
|
+
raise McMNoDataError
|
|
158
|
+
|
|
159
|
+
def getRequest(self, prepID):
|
|
160
|
+
"""
|
|
161
|
+
Get the request record which has, among other things,
|
|
162
|
+
the number of requested events
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
url = 'public/restapi/requests/get/%s' % prepID
|
|
166
|
+
res = self._getURL(url)
|
|
167
|
+
return res['results']
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
if __name__ == '__main__':
|
|
171
|
+
with McM() as mcm:
|
|
172
|
+
history = mcm.getHistory(prepID='BTV-Upg2023SHCAL14DR-00002')
|
|
173
|
+
request = mcm.getRequest(prepID='BTV-Upg2023SHCAL14DR-00002')
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# coding=utf-8
|
|
3
|
+
"""
|
|
4
|
+
Wrapper class, based on the PyCuRL module, providing an interface
|
|
5
|
+
to CERN MonIT Grafana APIs
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import division, print_function, absolute_import
|
|
8
|
+
from builtins import str, object
|
|
9
|
+
from future import standard_library
|
|
10
|
+
standard_library.install_aliases()
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
import json
|
|
14
|
+
from copy import copy
|
|
15
|
+
from pprint import pformat
|
|
16
|
+
from urllib.parse import urljoin
|
|
17
|
+
|
|
18
|
+
from WMCore.Services.pycurl_manager import RequestHandler
|
|
19
|
+
|
|
20
|
+
class Grafana(object):
|
|
21
|
+
"""
|
|
22
|
+
Service class providing functionality to CERN monitoring Grafana APIs
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, token, configDict=None):
|
|
26
|
+
"""
|
|
27
|
+
Constructs a Grafana object.
|
|
28
|
+
:param token: mandatory string with the key/token string
|
|
29
|
+
:param configDict: dictionary with extra parameters, such as:
|
|
30
|
+
logger: logger object
|
|
31
|
+
endpoint: string with the url/endpoint to be used
|
|
32
|
+
cacheduration: float with the cache duration, in hours
|
|
33
|
+
headers: dictionary with the headers to be used
|
|
34
|
+
"""
|
|
35
|
+
self._token = token
|
|
36
|
+
self.configDict = configDict or {}
|
|
37
|
+
self.configDict.setdefault('endpoint', "https://monit-grafana.cern.ch")
|
|
38
|
+
self.configDict.setdefault('cacheduration', 0) # in hours
|
|
39
|
+
if 'headers' not in self.configDict:
|
|
40
|
+
self.configDict.setdefault('headers', {})
|
|
41
|
+
self.configDict['headers'].update({"Accept": "application/json"})
|
|
42
|
+
self.configDict['headers'].update({"Content-Type": "application/json"})
|
|
43
|
+
|
|
44
|
+
self.logger = self.configDict.get("logger", logging.getLogger())
|
|
45
|
+
self.logger.info("MonIT service initialized with parameters: %s", self.configDict)
|
|
46
|
+
|
|
47
|
+
def updateToken(self, token):
|
|
48
|
+
"""
|
|
49
|
+
Update token to be used in requests made through this module
|
|
50
|
+
:param token: string with the new token
|
|
51
|
+
"""
|
|
52
|
+
self._token = token
|
|
53
|
+
|
|
54
|
+
def _postRequest(self, url, params, verb='POST', verbose=0):
|
|
55
|
+
"Helper function to POST request to given URL"
|
|
56
|
+
mgr = RequestHandler(logger=self.logger)
|
|
57
|
+
headers = copy(self.configDict['headers'])
|
|
58
|
+
headers.update({"Authorization": self._token})
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
data = mgr.getdata(url, params, headers, verb=verb, verbose=verbose)
|
|
62
|
+
return json.loads(data)
|
|
63
|
+
except Exception as exc:
|
|
64
|
+
self.logger.error("Failed to retrieve data from MonIT. Error: %s", str(exc))
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
def getAPIData(self, apiName, queryStr=''):
|
|
68
|
+
"""
|
|
69
|
+
_getAPIData_
|
|
70
|
+
|
|
71
|
+
Retrieve data from a given Grafana API and index name
|
|
72
|
+
:param apiName: string with the API name/number
|
|
73
|
+
:param queryStr: string with the whole query logic
|
|
74
|
+
:return: data as retrieved from Grafana
|
|
75
|
+
"""
|
|
76
|
+
uri = urljoin(self.configDict['endpoint'], "/api/datasources/proxy/%s/_msearch" % apiName)
|
|
77
|
+
return self._postRequest(uri, queryStr)
|
|
78
|
+
|
|
79
|
+
def getSSBData(self, pathName, metricName, indexName="monit_prod_cmssst_*",
|
|
80
|
+
apiName="9475", queryStr=''):
|
|
81
|
+
"""
|
|
82
|
+
_getSSBData_
|
|
83
|
+
|
|
84
|
+
Retrieve data from the SSB index in Grafana
|
|
85
|
+
:param pathName: string with the path to the correct metric, mapped from SSB as:
|
|
86
|
+
columnid=237 ProdStatus path="sts15min" data.prod_status
|
|
87
|
+
columnid=160 CPU bound path="scap15min" data.core_cpu_intensive
|
|
88
|
+
columnid=161 I/O bound path="scap15min" data.core_io_intensive
|
|
89
|
+
columnid=159 Prod Cores path="scap15min" data.core_production
|
|
90
|
+
columnid=136 Real Cores path="scap15min" data.core_max_used
|
|
91
|
+
columnid=107 Tape Pledge path="scap15min" data.tape_pledge
|
|
92
|
+
:param indexName: optional string with the grafana index name
|
|
93
|
+
:param apiName: optional string with the Grafana API number
|
|
94
|
+
:param queryStr: optional string with the full query logic
|
|
95
|
+
:return: dictionary with the site names and the metric value
|
|
96
|
+
"""
|
|
97
|
+
# TODO: allow flexible time range instead of the hardwired last 24h
|
|
98
|
+
# TODO: allow flexible number of rows, instead of the hardwired 500
|
|
99
|
+
results = {}
|
|
100
|
+
if metricName not in ('prod_status', 'core_cpu_intensive', 'core_io_intensive',
|
|
101
|
+
'core_production', 'core_max_used', 'tape_pledge'):
|
|
102
|
+
self.logger.error("SSB metric name '%s' is NOT supported.", metricName)
|
|
103
|
+
return results
|
|
104
|
+
|
|
105
|
+
uri = urljoin(self.configDict['endpoint'], "/api/datasources/proxy/%s/_msearch" % apiName)
|
|
106
|
+
if not queryStr:
|
|
107
|
+
### NOTE: these '\n' new lines are mandatory to get it working...
|
|
108
|
+
queryStr = '{"search_type":"query_then_fetch","ignore_unavailable":true,"index":["%s"]}\n' % indexName
|
|
109
|
+
queryStr += '{"size":500,"query":{"bool":{"filter":[{"range":{"metadata.timestamp":{"gte":"now-1d","lte":"now","format":"epoch_millis"}}},' \
|
|
110
|
+
'{"query_string":{"analyze_wildcard":true,' \
|
|
111
|
+
'"query":"metadata.type: ssbmetric AND metadata.type_prefix:raw AND metadata.monit_hdfs_path: %s"}}]}},' \
|
|
112
|
+
'"sort":{"metadata.timestamp":{"order":"desc","unmapped_type":"boolean"}},' \
|
|
113
|
+
'"script_fields":{},"docvalue_fields":["metadata.timestamp"]}\n' % pathName
|
|
114
|
+
self.logger.info("Calling API: %s with query: %s", uri, pformat(queryStr))
|
|
115
|
+
|
|
116
|
+
data = self._postRequest(uri, queryStr)
|
|
117
|
+
#self.logger.debug("Data retrieved: %s", pformat(data))
|
|
118
|
+
if data:
|
|
119
|
+
# now parse the ugly output
|
|
120
|
+
hits = data['responses'][0]['hits']['hits']
|
|
121
|
+
for item in hits:
|
|
122
|
+
site = item['_source']['data']['name']
|
|
123
|
+
metricValue = item['_source']['data'][metricName]
|
|
124
|
+
tStamp = int(item['sort'][0])
|
|
125
|
+
|
|
126
|
+
results.setdefault(site, {})
|
|
127
|
+
if tStamp > results[site].get("timeStamp", 0):
|
|
128
|
+
results[site][metricName] = metricValue
|
|
129
|
+
results[site]["timeStamp"] = tStamp
|
|
130
|
+
|
|
131
|
+
data = results
|
|
132
|
+
|
|
133
|
+
return data
|
|
File without changes
|