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
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
This module contains a few basic WMCore-related utilitarian
|
|
5
|
+
Rucio data structures and functions
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import random
|
|
9
|
+
|
|
10
|
+
RUCIO_VALID_PROJECT = ("Production", "RelVal", "Tier0", "Test", "User")
|
|
11
|
+
# grouping values are extracted from:
|
|
12
|
+
# https://github.com/rucio/rucio/blob/master/lib/rucio/common/schema/cms.py#L117
|
|
13
|
+
GROUPING_DSET = "DATASET"
|
|
14
|
+
GROUPING_ALL = "ALL"
|
|
15
|
+
RUCIO_RULES_PRIORITY = {"low": 2, "normal": 3, "high": 4, "reserved": 5}
|
|
16
|
+
# number of copies to be defined when creating replication rules
|
|
17
|
+
NUM_COPIES_DEFAULT = 1
|
|
18
|
+
NUM_COPIES_NANO = 2
|
|
19
|
+
|
|
20
|
+
def validateMetaData(did, metaDict, logger):
|
|
21
|
+
"""
|
|
22
|
+
This function can be extended in the future, for now it will only
|
|
23
|
+
validate the DID creation metadata, more specifically only the
|
|
24
|
+
"project" parameter
|
|
25
|
+
:param did: the DID that will be inserted
|
|
26
|
+
:param metaDict: a dictionary with all the DID metadata data to be inserted
|
|
27
|
+
:param logger: a logger object
|
|
28
|
+
:return: False if validation fails, otherwise True
|
|
29
|
+
"""
|
|
30
|
+
if metaDict.get("project", "Production") in RUCIO_VALID_PROJECT:
|
|
31
|
+
return True
|
|
32
|
+
msg = f"DID: {did} has an invalid 'project' meta-data value: {metaDict['project']}"
|
|
33
|
+
msg += f"The supported 'project' values are: {str(RUCIO_VALID_PROJECT)}"
|
|
34
|
+
logger.error(msg)
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def weightedChoice(rses, rseWeights):
|
|
39
|
+
"""
|
|
40
|
+
Given a list of items and their respective weights (quota in this case),
|
|
41
|
+
perform a weighted selection.
|
|
42
|
+
:param rses: a list of tuples with the RSE name and whether it requires approval
|
|
43
|
+
:param rseWeights: a list with RSE weights (quota)
|
|
44
|
+
:return: a tuple from the choices list
|
|
45
|
+
"""
|
|
46
|
+
listChoice = random.choices(population=rses, weights=rseWeights, k=1)
|
|
47
|
+
# return only the tuple, not the list with a tuple item
|
|
48
|
+
return listChoice[0]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def isTapeRSE(rseName):
|
|
52
|
+
"""
|
|
53
|
+
Given an RSE name, return True if it's a Tape RSE (rse_type=TAPE), otherwise False
|
|
54
|
+
:param rseName: string with the RSE name
|
|
55
|
+
:return: True or False
|
|
56
|
+
"""
|
|
57
|
+
# NOTE: a more reliable - but more expensive - way to know that would be
|
|
58
|
+
# to query `get_rse` and evaluate the rse_type parameter
|
|
59
|
+
return rseName.endswith("_Tape")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def dropTapeRSEs(listRSEs):
|
|
63
|
+
"""
|
|
64
|
+
Method to parse a list of RSE names and return only those that
|
|
65
|
+
are not a rse_type=TAPE, so in general only Disk endpoints
|
|
66
|
+
:param listRSEs: list with the RSE names
|
|
67
|
+
:return: a new list with only DISK RSE names
|
|
68
|
+
"""
|
|
69
|
+
diskRSEs = []
|
|
70
|
+
for rse in listRSEs:
|
|
71
|
+
if isTapeRSE(rse):
|
|
72
|
+
continue
|
|
73
|
+
diskRSEs.append(rse)
|
|
74
|
+
return diskRSEs
|
|
File without changes
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# coding=utf-8
|
|
3
|
+
"""
|
|
4
|
+
Rucio Consistency Monitor Service class developed on top of the native WMCore
|
|
5
|
+
Service APIs providing custom output and handling as necessary for the
|
|
6
|
+
CMS Workload Management system
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import division, print_function, absolute_import
|
|
10
|
+
from future import standard_library
|
|
11
|
+
|
|
12
|
+
from urllib.parse import urlencode
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import logging
|
|
16
|
+
|
|
17
|
+
from WMCore.Services.Service import Service
|
|
18
|
+
from Utils.Utilities import decodeBytesToUnicode
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
standard_library.install_aliases()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class RucioConMon(Service):
|
|
25
|
+
"""
|
|
26
|
+
API for dealing with retrieving information from Rucio Consistency Monitor
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, url, logger=None, configDict=None):
|
|
30
|
+
"""
|
|
31
|
+
Init method for the RucioConMon Class
|
|
32
|
+
"""
|
|
33
|
+
configDict = configDict or {}
|
|
34
|
+
configDict.setdefault('endpoint', url)
|
|
35
|
+
configDict.setdefault('cacheduration', 1) # in hours
|
|
36
|
+
configDict.setdefault('accept_type', 'application/json')
|
|
37
|
+
configDict.setdefault('content_type', 'application/json')
|
|
38
|
+
configDict['logger'] = logger if logger else logging.getLogger()
|
|
39
|
+
super(RucioConMon, self).__init__(configDict)
|
|
40
|
+
self['logger'].debug("Initializing RucioConMon with url: %s", self['endpoint'])
|
|
41
|
+
|
|
42
|
+
def _getResult(self, uri, callname="", clearCache=False, args=None):
|
|
43
|
+
"""
|
|
44
|
+
Either fetch data from the cache file or query the data-service
|
|
45
|
+
:param uri: The endpoint uri
|
|
46
|
+
:param callname: alias for caller function
|
|
47
|
+
:param clearCache: parameter to control the cache behavior
|
|
48
|
+
:param args: additional parameters to HTTP request call
|
|
49
|
+
:return: A dictionary
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
# NOTE: Unlike the common case we are not using the callname for building
|
|
53
|
+
# apiUrl. we are using it only for giving proper name of the cache
|
|
54
|
+
# file.
|
|
55
|
+
# NOTE: The 'callname' should not contain '/', otherwise the cache is
|
|
56
|
+
# tried to be created with a a non existing subdirectory structure
|
|
57
|
+
|
|
58
|
+
cachedApi = "%s.json" % callname
|
|
59
|
+
# apiUrl = '%s?json&preset=%s' % (uri, callname)
|
|
60
|
+
apiUrl = uri
|
|
61
|
+
|
|
62
|
+
self['logger'].debug('Fetching data from %s, with args %s', apiUrl, args)
|
|
63
|
+
if args:
|
|
64
|
+
apiUrl = "%s&%s" % (apiUrl, urlencode(args, doseq=True))
|
|
65
|
+
|
|
66
|
+
if clearCache:
|
|
67
|
+
self.clearCache(cachedApi, args)
|
|
68
|
+
with self.refreshCache(cachedApi, apiUrl, decoder=True, binary=False) as istream:
|
|
69
|
+
return json.load(istream)
|
|
70
|
+
|
|
71
|
+
def _getResultZipped(self, uri, callname="", clearCache=True):
|
|
72
|
+
"""
|
|
73
|
+
This method retrieves gzipped content, instead of the standard json format.
|
|
74
|
+
:param uri: The endpoint uri
|
|
75
|
+
:param callname: alias for caller function
|
|
76
|
+
:param clearCache: parameter to control the cache behavior
|
|
77
|
+
:return: yields a single record from the data retrieved
|
|
78
|
+
"""
|
|
79
|
+
cachedApi = callname
|
|
80
|
+
if clearCache:
|
|
81
|
+
self.clearCache(cachedApi)
|
|
82
|
+
|
|
83
|
+
with self.refreshCache(cachedApi, uri, decoder=False, binary=True) as istream:
|
|
84
|
+
for line in istream:
|
|
85
|
+
line = decodeBytesToUnicode(line).replace("\n", "")
|
|
86
|
+
yield line
|
|
87
|
+
|
|
88
|
+
def getRSEStats(self):
|
|
89
|
+
"""
|
|
90
|
+
Gets the latest statistics from the RucioConMon, together with the last
|
|
91
|
+
update timestamps for all RSEs known to CMS Rucio
|
|
92
|
+
:return: A dictionary
|
|
93
|
+
"""
|
|
94
|
+
uri = "stats"
|
|
95
|
+
rseStats = self._getResult(uri, callname='stats')
|
|
96
|
+
return rseStats
|
|
97
|
+
|
|
98
|
+
def getRSEUnmerged(self, rseName, zipped=False):
|
|
99
|
+
"""
|
|
100
|
+
Gets the list of all unmerged files in an RSE
|
|
101
|
+
:param rseName: The RSE whose list of unmerged files to be retrieved
|
|
102
|
+
:param zipped: If True the interface providing the zipped lists will be called
|
|
103
|
+
:return: a generator of unmerged files for the RSE in question
|
|
104
|
+
"""
|
|
105
|
+
# NOTE: The default API provided by Rucio Consistency Monitor is in a form of a
|
|
106
|
+
# zipped file/stream. Currently we are using the newly provided json API
|
|
107
|
+
# But in in case we figure out the data is too big we may need to
|
|
108
|
+
# implement the method with the zipped API and use disc cache for
|
|
109
|
+
# reading/streaming from file. This will prevent any set arithmetic
|
|
110
|
+
# in the future.
|
|
111
|
+
if zipped:
|
|
112
|
+
uri = "files?rse=%s&format=raw" % rseName
|
|
113
|
+
callname = '{}.zipped'.format(rseName)
|
|
114
|
+
rseUnmerged = self._getResultZipped(uri, callname=callname, clearCache=True)
|
|
115
|
+
else:
|
|
116
|
+
uri = "files?rse=%s&format=json" % rseName
|
|
117
|
+
callname = '{}.json'.format(rseName)
|
|
118
|
+
rseUnmerged = self._getResult(uri, callname=callname)
|
|
119
|
+
# now lazily return items
|
|
120
|
+
for item in rseUnmerged:
|
|
121
|
+
yield item
|
|
File without changes
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
_Service_
|
|
4
|
+
|
|
5
|
+
A Service talks to some http(s) accessible service that provides information and
|
|
6
|
+
caches the result of these queries. The cache will be refreshed if the file is
|
|
7
|
+
older than a timeout set in the instance of Service.
|
|
8
|
+
|
|
9
|
+
It has a cache path, cache duration, an endpoint (the url the
|
|
10
|
+
service exists on) a logger and an accept type (json, xml etc) and method
|
|
11
|
+
(GET/POST).
|
|
12
|
+
|
|
13
|
+
The Service satisfies two caching cases:
|
|
14
|
+
|
|
15
|
+
1. set a defined query, cache results, poll for new ones
|
|
16
|
+
2. use a changing query, cache results to a file depending on the query, poll
|
|
17
|
+
for new ones
|
|
18
|
+
|
|
19
|
+
Data maybe passed to the remote service either via adding the query string to
|
|
20
|
+
the URL (for GET's) or by passing a dictionary to either the service constructor
|
|
21
|
+
(case 1.) or by passing the data as a dictionary to the refreshCache,
|
|
22
|
+
forceCache, clearCache calls. By default the cache lasts 30 minutes.
|
|
23
|
+
|
|
24
|
+
Calling refreshCache/forceRefresh will return an open file object, the cache
|
|
25
|
+
file. Once done with it you should close the object.
|
|
26
|
+
|
|
27
|
+
The service has a default timeout to receive a response from the remote service
|
|
28
|
+
of 300 seconds. Over ride this by passing in a timeout via the configuration
|
|
29
|
+
dict, set to None if you want to turn off the timeout.
|
|
30
|
+
|
|
31
|
+
If you just want to retrieve the data without caching use the Requests class
|
|
32
|
+
directly.
|
|
33
|
+
|
|
34
|
+
The Service class provides two layers of caching:
|
|
35
|
+
1. Caching from httplib2 is provided via Request, this respects etag and
|
|
36
|
+
expires, but the cache will be lost if the service raises an exception or
|
|
37
|
+
similar.
|
|
38
|
+
2. Internal caching which respects an internal cache duration. If the remote
|
|
39
|
+
service fails to respond the second layer cache will be used until the cache
|
|
40
|
+
dies.
|
|
41
|
+
|
|
42
|
+
In tabular form:
|
|
43
|
+
|
|
44
|
+
httplib2 cache | yes | yes | no | no |
|
|
45
|
+
----------------+----------+----------+----------+------------+
|
|
46
|
+
service cache | no | yes | yes | no |
|
|
47
|
+
----------------+----------+----------+----------+------------+
|
|
48
|
+
result | cached | cached | cached | not cached |
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
from builtins import str
|
|
52
|
+
from future import standard_library
|
|
53
|
+
standard_library.install_aliases()
|
|
54
|
+
|
|
55
|
+
import datetime
|
|
56
|
+
import json
|
|
57
|
+
import logging
|
|
58
|
+
import os
|
|
59
|
+
import time
|
|
60
|
+
from io import BytesIO, StringIO
|
|
61
|
+
from http.client import HTTPException
|
|
62
|
+
|
|
63
|
+
from Utils.PythonVersion import PY3
|
|
64
|
+
from WMCore.Services.Requests import Requests, JSONRequests
|
|
65
|
+
from WMCore.WMException import WMException
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
from httplib2 import HttpLib2Error
|
|
69
|
+
except ImportError:
|
|
70
|
+
# Mock HttpLib2Error since we don't want that WMCore depend on httplib2 using pycurl
|
|
71
|
+
class HttpLib2Error(Exception):
|
|
72
|
+
pass
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def isfile(obj):
|
|
76
|
+
"""
|
|
77
|
+
Check whether obj is a file-like object (file, StringIO)"
|
|
78
|
+
"""
|
|
79
|
+
return hasattr(obj, 'flush')
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def cache_expired(cache, delta=0):
|
|
83
|
+
"""
|
|
84
|
+
Is the cache expired? At delta hours (default 0) in the future.
|
|
85
|
+
"""
|
|
86
|
+
if isfile(cache):
|
|
87
|
+
# already opened file-like object, assume we need to refresh
|
|
88
|
+
return True
|
|
89
|
+
else:
|
|
90
|
+
# then it's a file name
|
|
91
|
+
if not os.path.exists(cache):
|
|
92
|
+
return True
|
|
93
|
+
|
|
94
|
+
delta = datetime.timedelta(hours=delta)
|
|
95
|
+
t = datetime.datetime.now() - delta
|
|
96
|
+
# cache file mtime has been set to cache expiry time
|
|
97
|
+
if os.path.getmtime(cache) < time.mktime(t.timetuple()):
|
|
98
|
+
return True
|
|
99
|
+
|
|
100
|
+
return False
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _makeHash(inputdata):
|
|
104
|
+
"""
|
|
105
|
+
Turn the input data into json and hash the string. This is simple and
|
|
106
|
+
means that the input data must be json-serialisable, which is good.
|
|
107
|
+
"""
|
|
108
|
+
json_hash = json.dumps(inputdata)
|
|
109
|
+
return json_hash.__hash__()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class Service(dict):
|
|
113
|
+
def __init__(self, cfg_dict=None):
|
|
114
|
+
super(Service, self).__init__()
|
|
115
|
+
cfg_dict = cfg_dict or {}
|
|
116
|
+
# The following should read the configuration class
|
|
117
|
+
for a in ['endpoint']:
|
|
118
|
+
assert a in list(cfg_dict), "Can't have a service without a %s" % a
|
|
119
|
+
|
|
120
|
+
# if end point ends without '/', add that
|
|
121
|
+
if not cfg_dict['endpoint'].endswith('/'):
|
|
122
|
+
cfg_dict['endpoint'] = cfg_dict['endpoint'].strip() + '/'
|
|
123
|
+
|
|
124
|
+
# set up defaults
|
|
125
|
+
self.setdefault("inputdata", {})
|
|
126
|
+
self.setdefault("cacheduration", 0.5)
|
|
127
|
+
self.supportVerbList = ('GET', 'POST', 'PUT', 'DELETE')
|
|
128
|
+
# this value should be only set when whole service class uses
|
|
129
|
+
# the same verb ('GET', 'POST', 'PUT', 'DELETE')
|
|
130
|
+
self.setdefault("method", None)
|
|
131
|
+
|
|
132
|
+
# Set a timeout for the socket
|
|
133
|
+
self.setdefault("timeout", 300)
|
|
134
|
+
|
|
135
|
+
# then update with the incoming dict
|
|
136
|
+
self.update(cfg_dict)
|
|
137
|
+
|
|
138
|
+
self['service_name'] = self.__class__.__name__ # used for cache naming
|
|
139
|
+
|
|
140
|
+
# Get the request class, to instantiate later
|
|
141
|
+
# either passed as param to __init__, determine via scheme or default
|
|
142
|
+
if isinstance(self.get('requests'), type):
|
|
143
|
+
requests = self['requests']
|
|
144
|
+
elif self.get('accept_type') == "application/json" and self.get('content_type') == "application/json":
|
|
145
|
+
requests = JSONRequests
|
|
146
|
+
else:
|
|
147
|
+
requests = Requests
|
|
148
|
+
# Instantiate a Request
|
|
149
|
+
try:
|
|
150
|
+
self["requests"] = requests(cfg_dict['endpoint'], cfg_dict)
|
|
151
|
+
except WMException as ex:
|
|
152
|
+
msg = str(ex)
|
|
153
|
+
self["logger"].exception(msg)
|
|
154
|
+
raise
|
|
155
|
+
|
|
156
|
+
# cachepath will be modified - i.e. hostname added
|
|
157
|
+
self['cachepath'] = self["requests"]["cachepath"]
|
|
158
|
+
|
|
159
|
+
if 'logger' not in self:
|
|
160
|
+
if self['cachepath']:
|
|
161
|
+
logfile = os.path.join(self['cachepath'], '%s.log' % self.__class__.__name__.lower())
|
|
162
|
+
else:
|
|
163
|
+
logfile = None
|
|
164
|
+
logging.basicConfig(level=logging.DEBUG,
|
|
165
|
+
format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
|
|
166
|
+
datefmt='%m-%d %H:%M',
|
|
167
|
+
filename=logfile,
|
|
168
|
+
filemode='w')
|
|
169
|
+
self['logger'] = logging.getLogger(self.__class__.__name__)
|
|
170
|
+
self['requests']['logger'] = self['logger']
|
|
171
|
+
|
|
172
|
+
msg = "Service '%s' initialized with the following settings: %s"
|
|
173
|
+
self['logger'].debug(msg, self.__class__.__name__, self)
|
|
174
|
+
|
|
175
|
+
def cacheFileName(self, cachefile, verb='GET', inputdata=None):
|
|
176
|
+
"""
|
|
177
|
+
Calculate the cache filename for a given query.
|
|
178
|
+
"""
|
|
179
|
+
# if not caching to disk return StringIO object
|
|
180
|
+
if not self['cachepath'] or not cachefile:
|
|
181
|
+
return StringIO() if PY3 else BytesIO()
|
|
182
|
+
|
|
183
|
+
inputdata = inputdata or {}
|
|
184
|
+
if inputdata:
|
|
185
|
+
hash_ = _makeHash(inputdata)
|
|
186
|
+
else:
|
|
187
|
+
hash_ = _makeHash(self['inputdata'])
|
|
188
|
+
cachefile = "%s/%s_%s_%s" % (self["cachepath"], hash_, verb, cachefile)
|
|
189
|
+
|
|
190
|
+
return cachefile
|
|
191
|
+
|
|
192
|
+
def refreshCache(self, cachefile, url='', inputdata=None, openfile=True,
|
|
193
|
+
encoder=True, decoder=True, verb='GET', contentType=None, incoming_headers=None, binary=False):
|
|
194
|
+
"""
|
|
195
|
+
See if the cache has expired. If it has make a new request to the
|
|
196
|
+
service for the input data. Return the cachefile as an open file object.
|
|
197
|
+
If cachefile is None returns StringIO.
|
|
198
|
+
|
|
199
|
+
:param cachefile: cache file name
|
|
200
|
+
:param url: url value
|
|
201
|
+
:param inputdata: HTTP (JSON) payload string
|
|
202
|
+
:param openfile: flag to use open file, boolean
|
|
203
|
+
:param encoder: flag to use encoder, boolean
|
|
204
|
+
:param decoder: flag to use decode, boolean
|
|
205
|
+
:param verb: HTTP method, string
|
|
206
|
+
:param contentType: HTTP content type value, string
|
|
207
|
+
:param incoming_headers: set of HTTP headers, dict
|
|
208
|
+
:param binary: use binary mode to decode HTTP data, boolean
|
|
209
|
+
:return: either file object or StringIO
|
|
210
|
+
"""
|
|
211
|
+
inputdata = inputdata or {}
|
|
212
|
+
incoming_headers = incoming_headers or {}
|
|
213
|
+
verb = self._verbCheck(verb)
|
|
214
|
+
|
|
215
|
+
cachefile = self.cacheFileName(cachefile, verb, inputdata)
|
|
216
|
+
|
|
217
|
+
if cache_expired(cachefile, self["cacheduration"]):
|
|
218
|
+
self.getData(cachefile, url, inputdata, incoming_headers, encoder, decoder, verb, contentType, binary=binary)
|
|
219
|
+
else:
|
|
220
|
+
self['logger'].debug('Data is from the Service cache')
|
|
221
|
+
|
|
222
|
+
# cachefile may be filename or file object
|
|
223
|
+
if openfile and not isfile(cachefile):
|
|
224
|
+
if binary:
|
|
225
|
+
return open(cachefile, 'rb')
|
|
226
|
+
return open(cachefile, 'r', encoding='utf-8')
|
|
227
|
+
else:
|
|
228
|
+
return cachefile
|
|
229
|
+
|
|
230
|
+
def forceRefresh(self, cachefile, url='', inputdata=None, openfile=True,
|
|
231
|
+
encoder=True, decoder=True, verb='GET',
|
|
232
|
+
contentType=None, incoming_headers=None, binary=False):
|
|
233
|
+
"""
|
|
234
|
+
Make a new request to the service for the input data, regardless of the
|
|
235
|
+
cache state. Return the cachefile as an open file object.
|
|
236
|
+
If cachefile is None returns StringIO.
|
|
237
|
+
|
|
238
|
+
:param cachefile: cache file name
|
|
239
|
+
:param url: url value
|
|
240
|
+
:param inputdata: HTTP (JSON) payload string
|
|
241
|
+
:param openfile: flag to use open file, boolean
|
|
242
|
+
:param encoder: flag to use encoder, boolean
|
|
243
|
+
:param decoder: flag to use decode, boolean
|
|
244
|
+
:param verb: HTTP method, string
|
|
245
|
+
:param contentType: HTTP content type value, string
|
|
246
|
+
:param incoming_headers: set of HTTP headers, dict
|
|
247
|
+
:param binary: use binary mode to decode HTTP data, boolean
|
|
248
|
+
:return: either file object or StringIO
|
|
249
|
+
"""
|
|
250
|
+
inputdata = inputdata or {}
|
|
251
|
+
incoming_headers = incoming_headers or {}
|
|
252
|
+
verb = self._verbCheck(verb)
|
|
253
|
+
|
|
254
|
+
cachefile = self.cacheFileName(cachefile, verb, inputdata)
|
|
255
|
+
|
|
256
|
+
self['logger'].debug("Forcing cache refresh of %s" % cachefile)
|
|
257
|
+
incoming_headers.update({'cache-control': 'no-cache'})
|
|
258
|
+
self.getData(cachefile, url, inputdata, incoming_headers,
|
|
259
|
+
encoder, decoder, verb, contentType, force_refresh=True, binary=binary)
|
|
260
|
+
if openfile and not isfile(cachefile):
|
|
261
|
+
if binary:
|
|
262
|
+
return open(cachefile, 'rb')
|
|
263
|
+
return open(cachefile, 'r', encoding='utf-8')
|
|
264
|
+
else:
|
|
265
|
+
return cachefile
|
|
266
|
+
|
|
267
|
+
def clearCache(self, cachefile, inputdata=None, verb='GET'):
|
|
268
|
+
"""
|
|
269
|
+
Delete the cache file and the httplib2 cache.
|
|
270
|
+
"""
|
|
271
|
+
if not self['cachepath'] or not cachefile:
|
|
272
|
+
# nothing to clear
|
|
273
|
+
return
|
|
274
|
+
|
|
275
|
+
inputdata = inputdata or {}
|
|
276
|
+
verb = self._verbCheck(verb)
|
|
277
|
+
os.system("/bin/rm -f %s/*" % self['requests']['req_cache_path'])
|
|
278
|
+
cachefile = self.cacheFileName(cachefile, verb, inputdata)
|
|
279
|
+
try:
|
|
280
|
+
if not isfile(cachefile):
|
|
281
|
+
os.remove(cachefile)
|
|
282
|
+
except OSError: # File doesn't exist
|
|
283
|
+
return
|
|
284
|
+
|
|
285
|
+
def getData(self, cachefile, url, inputdata=None, incoming_headers=None,
|
|
286
|
+
encoder=True, decoder=True,
|
|
287
|
+
verb='GET', contentType=None, force_refresh=False, binary=False):
|
|
288
|
+
"""
|
|
289
|
+
Takes the already generated *full* path to cachefile and the url of the
|
|
290
|
+
resource. Don't need to call self.cacheFileName(cachefile, verb, inputdata)
|
|
291
|
+
here.
|
|
292
|
+
|
|
293
|
+
If cachefile is StringIO append to that
|
|
294
|
+
|
|
295
|
+
:param cachefile: cache file name
|
|
296
|
+
:param url: url value
|
|
297
|
+
:param inputdata: HTTP (JSON) payload string
|
|
298
|
+
:param encoder: flag to use encoder, boolean
|
|
299
|
+
:param decoder: flag to use decode, boolean
|
|
300
|
+
:param verb: HTTP method, string
|
|
301
|
+
:param contentType: HTTP content type value, string
|
|
302
|
+
:param force_refresh: refresh internal cache, boolean
|
|
303
|
+
:param binary: use binary mode to decode HTTP data, boolean
|
|
304
|
+
:return: None
|
|
305
|
+
"""
|
|
306
|
+
inputdata = inputdata or {}
|
|
307
|
+
incoming_headers = incoming_headers or {}
|
|
308
|
+
verb = self._verbCheck(verb)
|
|
309
|
+
|
|
310
|
+
try:
|
|
311
|
+
# Get the data
|
|
312
|
+
if not inputdata:
|
|
313
|
+
inputdata = self["inputdata"]
|
|
314
|
+
self['logger'].debug('getData: \n\turl: %s\n\tverb: %s\n\tincoming_headers: %s\n\tdata: %s',
|
|
315
|
+
url, verb, incoming_headers, inputdata)
|
|
316
|
+
# self['logger'].debug('getData: \n\turl: %s\n\tdata: %s' % \
|
|
317
|
+
# (url, inputdata))
|
|
318
|
+
data, dummyStatus, dummyReason, from_cache = self["requests"].makeRequest(uri=url,
|
|
319
|
+
verb=verb,
|
|
320
|
+
data=inputdata,
|
|
321
|
+
incoming_headers=incoming_headers,
|
|
322
|
+
encoder=encoder,
|
|
323
|
+
decoder=decoder,
|
|
324
|
+
contentType=contentType)
|
|
325
|
+
if from_cache:
|
|
326
|
+
# If it's coming from the cache we don't need to write it to the
|
|
327
|
+
# second cache, or do we?
|
|
328
|
+
self['logger'].debug('Data is from the Requests cache')
|
|
329
|
+
else:
|
|
330
|
+
# getData have done that for us
|
|
331
|
+
if isfile(cachefile):
|
|
332
|
+
cachefile.write(data)
|
|
333
|
+
cachefile.seek(0, 0) # return to beginning of file
|
|
334
|
+
elif binary:
|
|
335
|
+
with open(cachefile, 'wb') as f:
|
|
336
|
+
f.write(data)
|
|
337
|
+
elif isinstance(data, (dict, list)):
|
|
338
|
+
with open(cachefile, 'w', encoding='utf-8') as f:
|
|
339
|
+
f.write(json.dumps(data))
|
|
340
|
+
else:
|
|
341
|
+
with open(cachefile, 'w', encoding='utf-8') as f:
|
|
342
|
+
f.write(data)
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
except (IOError, HttpLib2Error, HTTPException) as he:
|
|
346
|
+
#
|
|
347
|
+
# Overly complicated exception handling. This is due to a request
|
|
348
|
+
# from *Ops that it is very clear that data is is being returned
|
|
349
|
+
# from a cachefile, and that cachefiles can be good/stale/dead.
|
|
350
|
+
#
|
|
351
|
+
if force_refresh or isfile(cachefile) or not os.path.exists(cachefile):
|
|
352
|
+
msg = 'The cachefile %s does not exist and the service at %s'
|
|
353
|
+
msg = msg % (cachefile, self["requests"]['host'] + url)
|
|
354
|
+
if hasattr(he, 'status') and hasattr(he, 'reason'):
|
|
355
|
+
msg += ' is unavailable - it returned %s because %s' % (he.status,
|
|
356
|
+
he.reason)
|
|
357
|
+
if hasattr(he, 'result'):
|
|
358
|
+
msg += ' with result: %s\n' % he.result
|
|
359
|
+
else:
|
|
360
|
+
msg += ' raised a %s when accessed' % he.__repr__()
|
|
361
|
+
self['logger'].warning(msg)
|
|
362
|
+
raise he
|
|
363
|
+
else:
|
|
364
|
+
cache_dead = cache_expired(cachefile, delta=self["cacheduration"])
|
|
365
|
+
if cache_dead:
|
|
366
|
+
msg = 'The cachefile %s is dead (older than %s hours of cache duration), '
|
|
367
|
+
msg += 'and the service at %s '
|
|
368
|
+
msg = msg % (cachefile, self["cacheduration"], url)
|
|
369
|
+
if hasattr(he, 'status') and hasattr(he, 'reason'):
|
|
370
|
+
msg += 'is unavailable - it returned %s because %s' % (he.status, he.reason)
|
|
371
|
+
else:
|
|
372
|
+
msg += 'raised a %s when accessed' % he.__repr__()
|
|
373
|
+
self['logger'].warning(msg)
|
|
374
|
+
raise he
|
|
375
|
+
if self.get('usestalecache', False):
|
|
376
|
+
# then we can return data from the cache file, without raising an exception
|
|
377
|
+
# but with a suitable message in the log
|
|
378
|
+
msg = 'Returning stale cache data from %s, the service at ' % cachefile
|
|
379
|
+
if hasattr(he, 'status') and hasattr(he, 'reason'):
|
|
380
|
+
msg += '%s returned %s because %s' % (he.url, he.status, he.reason)
|
|
381
|
+
else:
|
|
382
|
+
msg += '%s raised a %s when accessed' % (url, he.__repr__())
|
|
383
|
+
self['logger'].warning(msg)
|
|
384
|
+
else:
|
|
385
|
+
# Cache is not dead, but Service is configured to not return stale data.
|
|
386
|
+
msg = 'The cachefile %s is stale and the service at %s ' % (cachefile, url)
|
|
387
|
+
if hasattr(he, 'status') and hasattr(he, 'reason'):
|
|
388
|
+
msg += 'is unavailable - it returned %s because %s' % (he.status, he.reason)
|
|
389
|
+
else:
|
|
390
|
+
msg += 'raised a %s when accessed' % he.__repr__()
|
|
391
|
+
self['logger'].warning(msg)
|
|
392
|
+
raise he
|
|
393
|
+
|
|
394
|
+
def _verbCheck(self, verb='GET'):
|
|
395
|
+
if verb.upper() in self.supportVerbList:
|
|
396
|
+
return verb.upper()
|
|
397
|
+
elif self['method'].upper() in self.supportVerbList:
|
|
398
|
+
return self['method'].upper()
|
|
399
|
+
else:
|
|
400
|
+
raise TypeError('verb parameter needs to be set')
|
|
File without changes
|