DIRAC 9.0.0a42__py3-none-any.whl → 9.0.7__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.
- DIRAC/AccountingSystem/Client/AccountingCLI.py +0 -140
- DIRAC/AccountingSystem/Client/DataStoreClient.py +0 -13
- DIRAC/AccountingSystem/Client/Types/BaseAccountingType.py +0 -7
- DIRAC/AccountingSystem/ConfigTemplate.cfg +0 -5
- DIRAC/AccountingSystem/Service/DataStoreHandler.py +0 -72
- DIRAC/ConfigurationSystem/Client/Helpers/CSGlobals.py +0 -9
- DIRAC/ConfigurationSystem/Client/Helpers/Registry.py +38 -26
- DIRAC/ConfigurationSystem/Client/Helpers/Resources.py +11 -43
- DIRAC/ConfigurationSystem/Client/Helpers/test/Test_Helpers.py +0 -16
- DIRAC/ConfigurationSystem/Client/LocalConfiguration.py +14 -8
- DIRAC/ConfigurationSystem/Client/PathFinder.py +47 -8
- DIRAC/ConfigurationSystem/Client/SyncPlugins/CERNLDAPSyncPlugin.py +4 -1
- DIRAC/ConfigurationSystem/Client/VOMS2CSSynchronizer.py +32 -19
- DIRAC/ConfigurationSystem/Client/test/Test_PathFinder.py +41 -1
- DIRAC/ConfigurationSystem/private/RefresherBase.py +4 -2
- DIRAC/Core/Base/API.py +4 -7
- DIRAC/Core/Base/SQLAlchemyDB.py +1 -0
- DIRAC/Core/DISET/ServiceReactor.py +11 -3
- DIRAC/Core/DISET/private/BaseClient.py +1 -2
- DIRAC/Core/DISET/private/Transports/M2SSLTransport.py +9 -7
- DIRAC/Core/DISET/private/Transports/SSL/M2Utils.py +3 -1
- DIRAC/Core/LCG/GOCDBClient.py +5 -7
- DIRAC/Core/Security/DiracX.py +31 -17
- DIRAC/Core/Security/IAMService.py +5 -10
- DIRAC/Core/Security/Locations.py +27 -18
- DIRAC/Core/Security/ProxyInfo.py +9 -5
- DIRAC/Core/Security/VOMSService.py +2 -4
- DIRAC/Core/Security/m2crypto/X509Certificate.py +4 -6
- DIRAC/Core/Security/m2crypto/asn1_utils.py +17 -5
- DIRAC/Core/Security/test/test_diracx_token_from_pem.py +161 -0
- DIRAC/Core/Tornado/Client/ClientSelector.py +4 -1
- DIRAC/Core/Tornado/Server/TornadoService.py +1 -1
- DIRAC/Core/Utilities/CGroups2.py +328 -0
- DIRAC/Core/Utilities/ClassAd/ClassAdLight.py +4 -290
- DIRAC/Core/Utilities/DErrno.py +5 -309
- DIRAC/Core/Utilities/Extensions.py +10 -1
- DIRAC/Core/Utilities/File.py +1 -1
- DIRAC/Core/Utilities/Graphs/GraphData.py +1 -1
- DIRAC/Core/Utilities/Graphs/GraphUtilities.py +6 -1
- DIRAC/Core/Utilities/JDL.py +1 -195
- DIRAC/Core/Utilities/List.py +1 -124
- DIRAC/Core/Utilities/MySQL.py +103 -99
- DIRAC/Core/Utilities/Os.py +32 -1
- DIRAC/Core/Utilities/Platform.py +2 -107
- DIRAC/Core/Utilities/Proxy.py +0 -4
- DIRAC/Core/Utilities/ReturnValues.py +7 -252
- DIRAC/Core/Utilities/StateMachine.py +12 -178
- DIRAC/Core/Utilities/Subprocess.py +35 -14
- DIRAC/Core/Utilities/TimeUtilities.py +10 -253
- DIRAC/Core/Utilities/test/Test_JDL.py +0 -3
- DIRAC/Core/Utilities/test/Test_Profiler.py +20 -20
- DIRAC/Core/scripts/dirac_agent.py +1 -1
- DIRAC/Core/scripts/dirac_apptainer_exec.py +72 -46
- DIRAC/Core/scripts/dirac_configure.py +1 -3
- DIRAC/Core/scripts/dirac_install_db.py +24 -6
- DIRAC/Core/scripts/dirac_platform.py +1 -92
- DIRAC/DataManagementSystem/Agent/FTS3Agent.py +8 -7
- DIRAC/DataManagementSystem/Agent/RequestOperations/RemoveFile.py +7 -6
- DIRAC/DataManagementSystem/Client/FTS3Job.py +71 -34
- DIRAC/DataManagementSystem/DB/FTS3DB.py +7 -3
- DIRAC/DataManagementSystem/DB/FileCatalogComponents/DatasetManager/DatasetManager.py +1 -1
- DIRAC/DataManagementSystem/DB/FileCatalogDB.sql +9 -9
- DIRAC/DataManagementSystem/DB/FileCatalogWithFkAndPsDB.sql +9 -9
- DIRAC/DataManagementSystem/Utilities/DMSHelpers.py +6 -2
- DIRAC/DataManagementSystem/scripts/dirac_admin_allow_se.py +13 -8
- DIRAC/DataManagementSystem/scripts/dirac_admin_ban_se.py +13 -8
- DIRAC/DataManagementSystem/scripts/dirac_dms_create_moving_request.py +2 -0
- DIRAC/DataManagementSystem/scripts/dirac_dms_protocol_matrix.py +0 -1
- DIRAC/FrameworkSystem/Client/BundleDeliveryClient.py +2 -7
- DIRAC/FrameworkSystem/Client/ComponentInstaller.py +9 -4
- DIRAC/FrameworkSystem/Client/ProxyManagerClient.py +5 -2
- DIRAC/FrameworkSystem/Client/SystemAdministratorClientCLI.py +11 -6
- DIRAC/FrameworkSystem/ConfigTemplate.cfg +2 -0
- DIRAC/FrameworkSystem/DB/AuthDB.py +3 -3
- DIRAC/FrameworkSystem/DB/InstalledComponentsDB.py +4 -4
- DIRAC/FrameworkSystem/DB/ProxyDB.py +11 -3
- DIRAC/FrameworkSystem/DB/TokenDB.py +1 -1
- DIRAC/FrameworkSystem/Service/ProxyManagerHandler.py +8 -6
- DIRAC/FrameworkSystem/Utilities/MonitoringUtilities.py +2 -19
- DIRAC/FrameworkSystem/Utilities/TokenManagementUtilities.py +3 -2
- DIRAC/FrameworkSystem/Utilities/diracx.py +36 -14
- DIRAC/FrameworkSystem/private/authorization/AuthServer.py +2 -2
- DIRAC/FrameworkSystem/scripts/dirac_admin_update_pilot.py +18 -11
- DIRAC/FrameworkSystem/scripts/dirac_login.py +2 -2
- DIRAC/FrameworkSystem/scripts/dirac_proxy_init.py +7 -8
- DIRAC/Interfaces/API/Dirac.py +27 -15
- DIRAC/Interfaces/API/DiracAdmin.py +45 -17
- DIRAC/Interfaces/API/Job.py +9 -13
- DIRAC/Interfaces/scripts/dirac_admin_allow_site.py +12 -18
- DIRAC/Interfaces/scripts/dirac_admin_ban_site.py +12 -10
- DIRAC/Interfaces/scripts/dirac_admin_get_site_mask.py +4 -13
- DIRAC/Interfaces/scripts/dirac_admin_reset_job.py +3 -6
- DIRAC/Interfaces/scripts/dirac_wms_job_parameters.py +0 -1
- DIRAC/MonitoringSystem/Client/Types/WMSHistory.py +4 -0
- DIRAC/MonitoringSystem/Client/WebAppClient.py +26 -0
- DIRAC/MonitoringSystem/ConfigTemplate.cfg +9 -0
- DIRAC/MonitoringSystem/DB/MonitoringDB.py +6 -25
- DIRAC/MonitoringSystem/Service/MonitoringHandler.py +0 -33
- DIRAC/MonitoringSystem/Service/WebAppHandler.py +599 -0
- DIRAC/MonitoringSystem/private/MainReporter.py +0 -3
- DIRAC/ProductionSystem/DB/ProductionDB.sql +4 -4
- DIRAC/ProductionSystem/scripts/dirac_prod_get.py +2 -2
- DIRAC/ProductionSystem/scripts/dirac_prod_get_all.py +2 -2
- DIRAC/ProductionSystem/scripts/dirac_prod_get_trans.py +2 -3
- DIRAC/RequestManagementSystem/Agent/RequestExecutingAgent.py +8 -6
- DIRAC/RequestManagementSystem/Agent/RequestOperations/ForwardDISET.py +2 -14
- DIRAC/RequestManagementSystem/Client/ReqClient.py +66 -13
- DIRAC/RequestManagementSystem/ConfigTemplate.cfg +6 -6
- DIRAC/RequestManagementSystem/DB/RequestDB.py +10 -5
- DIRAC/RequestManagementSystem/DB/test/RMSTestScenari.py +2 -0
- DIRAC/RequestManagementSystem/private/RequestValidator.py +40 -46
- DIRAC/ResourceStatusSystem/Client/SiteStatus.py +4 -2
- DIRAC/ResourceStatusSystem/Command/FreeDiskSpaceCommand.py +3 -1
- DIRAC/ResourceStatusSystem/DB/ResourceManagementDB.py +8 -8
- DIRAC/ResourceStatusSystem/DB/ResourceStatusDB.py +2 -2
- DIRAC/ResourceStatusSystem/Utilities/CSHelpers.py +2 -31
- DIRAC/ResourceStatusSystem/scripts/dirac_rss_set_status.py +30 -12
- DIRAC/Resources/Catalog/RucioFileCatalogClient.py +195 -1
- DIRAC/Resources/Catalog/test/Test_RucioFileCatalogClient.py +181 -0
- DIRAC/Resources/Computing/AREXComputingElement.py +25 -8
- DIRAC/Resources/Computing/BatchSystems/Condor.py +126 -108
- DIRAC/Resources/Computing/BatchSystems/SLURM.py +5 -1
- DIRAC/Resources/Computing/BatchSystems/test/Test_SLURM.py +46 -0
- DIRAC/Resources/Computing/ComputingElement.py +1 -1
- DIRAC/Resources/Computing/HTCondorCEComputingElement.py +44 -44
- DIRAC/Resources/Computing/InProcessComputingElement.py +4 -2
- DIRAC/Resources/Computing/LocalComputingElement.py +1 -18
- DIRAC/Resources/Computing/SSHBatchComputingElement.py +1 -17
- DIRAC/Resources/Computing/SSHComputingElement.py +1 -18
- DIRAC/Resources/Computing/SingularityComputingElement.py +19 -5
- DIRAC/Resources/Computing/test/Test_HTCondorCEComputingElement.py +67 -49
- DIRAC/Resources/Computing/test/Test_PoolComputingElement.py +2 -1
- DIRAC/Resources/IdProvider/CheckInIdProvider.py +13 -0
- DIRAC/Resources/IdProvider/IdProviderFactory.py +11 -3
- DIRAC/Resources/MessageQueue/StompMQConnector.py +1 -1
- DIRAC/Resources/Storage/GFAL2_StorageBase.py +24 -15
- DIRAC/Resources/Storage/OccupancyPlugins/WLCGAccountingHTTPJson.py +1 -3
- DIRAC/Resources/Storage/StorageBase.py +4 -2
- DIRAC/Resources/Storage/StorageElement.py +6 -7
- DIRAC/StorageManagementSystem/DB/StorageManagementDB.sql +2 -2
- DIRAC/TransformationSystem/Agent/TaskManagerAgentBase.py +10 -16
- DIRAC/TransformationSystem/Agent/TransformationAgent.py +22 -1
- DIRAC/TransformationSystem/Agent/TransformationCleaningAgent.py +16 -16
- DIRAC/TransformationSystem/Client/TaskManager.py +2 -4
- DIRAC/TransformationSystem/Client/Transformation.py +6 -7
- DIRAC/TransformationSystem/Client/TransformationClient.py +21 -11
- DIRAC/TransformationSystem/Client/Utilities.py +9 -0
- DIRAC/TransformationSystem/DB/TransformationDB.py +11 -14
- DIRAC/TransformationSystem/DB/TransformationDB.sql +9 -9
- DIRAC/TransformationSystem/Service/TransformationManagerHandler.py +0 -333
- DIRAC/TransformationSystem/Utilities/ReplicationCLIParameters.py +3 -3
- DIRAC/TransformationSystem/Utilities/TransformationInfo.py +7 -5
- DIRAC/TransformationSystem/scripts/dirac_production_runjoblocal.py +2 -4
- DIRAC/TransformationSystem/test/Test_TransformationInfo.py +22 -15
- DIRAC/TransformationSystem/test/Test_replicationTransformation.py +5 -6
- DIRAC/Workflow/Modules/test/Test_Modules.py +5 -0
- DIRAC/WorkloadManagementSystem/Agent/JobAgent.py +38 -26
- DIRAC/WorkloadManagementSystem/Agent/JobCleaningAgent.py +12 -8
- DIRAC/WorkloadManagementSystem/Agent/PilotSyncAgent.py +4 -3
- DIRAC/WorkloadManagementSystem/Agent/PushJobAgent.py +13 -13
- DIRAC/WorkloadManagementSystem/Agent/SiteDirector.py +18 -14
- DIRAC/WorkloadManagementSystem/Agent/StalledJobAgent.py +18 -51
- DIRAC/WorkloadManagementSystem/Agent/StatesAccountingAgent.py +41 -1
- DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_JobAgent.py +45 -4
- DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_JobCleaningAgent.py +7 -9
- DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_PushJobAgent.py +1 -0
- DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_SiteDirector.py +9 -2
- DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_StalledJobAgent.py +4 -5
- DIRAC/WorkloadManagementSystem/Client/DownloadInputData.py +9 -9
- DIRAC/WorkloadManagementSystem/Client/InputDataResolution.py +6 -6
- DIRAC/WorkloadManagementSystem/Client/JobMonitoringClient.py +10 -11
- DIRAC/WorkloadManagementSystem/Client/JobReport.py +1 -1
- DIRAC/WorkloadManagementSystem/Client/JobState/CachedJobState.py +3 -0
- DIRAC/WorkloadManagementSystem/Client/JobState/JobManifest.py +32 -261
- DIRAC/WorkloadManagementSystem/Client/JobState/JobState.py +6 -0
- DIRAC/WorkloadManagementSystem/Client/JobStateUpdateClient.py +3 -0
- DIRAC/WorkloadManagementSystem/Client/JobStatus.py +8 -152
- DIRAC/WorkloadManagementSystem/Client/PoolXMLSlice.py +12 -19
- DIRAC/WorkloadManagementSystem/Client/SandboxStoreClient.py +25 -38
- DIRAC/WorkloadManagementSystem/Client/WMSClient.py +2 -3
- DIRAC/WorkloadManagementSystem/Client/test/Test_Client_DownloadInputData.py +29 -0
- DIRAC/WorkloadManagementSystem/ConfigTemplate.cfg +4 -8
- DIRAC/WorkloadManagementSystem/DB/JobDB.py +89 -132
- DIRAC/WorkloadManagementSystem/DB/JobDB.sql +8 -8
- DIRAC/WorkloadManagementSystem/DB/JobDBUtils.py +18 -147
- DIRAC/WorkloadManagementSystem/DB/JobLoggingDB.py +19 -6
- DIRAC/WorkloadManagementSystem/DB/JobParametersDB.py +9 -9
- DIRAC/WorkloadManagementSystem/DB/PilotAgentsDB.py +16 -5
- DIRAC/WorkloadManagementSystem/DB/PilotAgentsDB.sql +3 -3
- DIRAC/WorkloadManagementSystem/DB/SandboxMetadataDB.py +44 -82
- DIRAC/WorkloadManagementSystem/DB/StatusUtils.py +125 -0
- DIRAC/WorkloadManagementSystem/DB/tests/Test_JobDB.py +1 -1
- DIRAC/WorkloadManagementSystem/DB/tests/Test_StatusUtils.py +28 -0
- DIRAC/WorkloadManagementSystem/Executor/JobSanity.py +5 -4
- DIRAC/WorkloadManagementSystem/Executor/JobScheduling.py +4 -0
- DIRAC/WorkloadManagementSystem/FutureClient/JobStateUpdateClient.py +75 -33
- DIRAC/WorkloadManagementSystem/JobWrapper/JobWrapper.py +22 -11
- DIRAC/WorkloadManagementSystem/JobWrapper/JobWrapperTemplate.py +9 -10
- DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapper.py +60 -10
- DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapperTemplate.py +4 -0
- DIRAC/WorkloadManagementSystem/Service/JobManagerHandler.py +33 -154
- DIRAC/WorkloadManagementSystem/Service/JobMonitoringHandler.py +5 -323
- DIRAC/WorkloadManagementSystem/Service/JobStateUpdateHandler.py +0 -16
- DIRAC/WorkloadManagementSystem/Service/PilotManagerHandler.py +6 -103
- DIRAC/WorkloadManagementSystem/Service/SandboxStoreHandler.py +7 -53
- DIRAC/WorkloadManagementSystem/Service/WMSAdministratorHandler.py +16 -79
- DIRAC/WorkloadManagementSystem/Service/WMSUtilities.py +4 -18
- DIRAC/WorkloadManagementSystem/Utilities/JobModel.py +28 -209
- DIRAC/WorkloadManagementSystem/Utilities/JobParameters.py +65 -3
- DIRAC/WorkloadManagementSystem/Utilities/JobStatusUtility.py +2 -64
- DIRAC/WorkloadManagementSystem/Utilities/ParametricJob.py +7 -171
- DIRAC/WorkloadManagementSystem/Utilities/PilotCStoJSONSynchronizer.py +73 -7
- DIRAC/WorkloadManagementSystem/Utilities/PilotWrapper.py +41 -11
- DIRAC/WorkloadManagementSystem/Utilities/RemoteRunner.py +16 -0
- DIRAC/WorkloadManagementSystem/Utilities/Utils.py +36 -1
- DIRAC/WorkloadManagementSystem/Utilities/jobAdministration.py +15 -0
- DIRAC/WorkloadManagementSystem/Utilities/test/Test_JobModel.py +1 -15
- DIRAC/WorkloadManagementSystem/Utilities/test/Test_ParametricJob.py +45 -128
- DIRAC/WorkloadManagementSystem/Utilities/test/Test_PilotWrapper.py +16 -0
- DIRAC/WorkloadManagementSystem/scripts/dirac_jobexec.py +7 -2
- DIRAC/WorkloadManagementSystem/scripts/dirac_wms_pilot_job_info.py +1 -1
- DIRAC/__init__.py +62 -60
- DIRAC/tests/Utilities/testJobDefinitions.py +22 -28
- {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info}/METADATA +8 -5
- {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info}/RECORD +229 -228
- {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info}/WHEEL +1 -1
- {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info}/entry_points.txt +0 -3
- DIRAC/Core/Utilities/test/Test_List.py +0 -150
- DIRAC/Core/Utilities/test/Test_Time.py +0 -88
- DIRAC/Resources/Computing/PilotBundle.py +0 -70
- DIRAC/TransformationSystem/scripts/dirac_transformation_archive.py +0 -30
- DIRAC/TransformationSystem/scripts/dirac_transformation_clean.py +0 -30
- DIRAC/TransformationSystem/scripts/dirac_transformation_remove_output.py +0 -30
- DIRAC/WorkloadManagementSystem/Utilities/test/Test_JobManager.py +0 -58
- {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info/licenses}/LICENSE +0 -0
- {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info}/top_level.txt +0 -0
DIRAC/Core/Utilities/List.py
CHANGED
|
@@ -1,124 +1 @@
|
|
|
1
|
-
|
|
2
|
-
By default on Error they return None.
|
|
3
|
-
"""
|
|
4
|
-
import random
|
|
5
|
-
import sys
|
|
6
|
-
from typing import Any
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def uniqueElements(aList: list) -> list:
|
|
10
|
-
"""Utility to retrieve list of unique elements in a list (order is kept)."""
|
|
11
|
-
|
|
12
|
-
# Use dict.fromkeys instead of set ensure the order is preserved
|
|
13
|
-
return list(dict.fromkeys(aList))
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def appendUnique(aList: list, anObject: Any):
|
|
17
|
-
"""Append to list if object does not exist.
|
|
18
|
-
|
|
19
|
-
:param aList: list of elements
|
|
20
|
-
:param anObject: object you want to append
|
|
21
|
-
"""
|
|
22
|
-
if anObject not in aList:
|
|
23
|
-
aList.append(anObject)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def fromChar(inputString: str, sepChar: str = ","):
|
|
27
|
-
"""Generates a list splitting a string by the required character(s)
|
|
28
|
-
resulting string items are stripped and empty items are removed.
|
|
29
|
-
|
|
30
|
-
:param inputString: list serialised to string
|
|
31
|
-
:param sepChar: separator
|
|
32
|
-
:return: list of strings or None if sepChar has a wrong type
|
|
33
|
-
"""
|
|
34
|
-
# to prevent getting an empty String as argument
|
|
35
|
-
if not (isinstance(inputString, str) and isinstance(sepChar, str) and sepChar):
|
|
36
|
-
return None
|
|
37
|
-
return [fieldString.strip() for fieldString in inputString.split(sepChar) if len(fieldString.strip()) > 0]
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def randomize(aList: list) -> list:
|
|
41
|
-
"""Return a randomly sorted list.
|
|
42
|
-
|
|
43
|
-
:param aList: list to permute
|
|
44
|
-
"""
|
|
45
|
-
tmpList = list(aList)
|
|
46
|
-
random.shuffle(tmpList)
|
|
47
|
-
return tmpList
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def pop(aList, popElement):
|
|
51
|
-
"""Pop the first element equal to popElement from the list.
|
|
52
|
-
|
|
53
|
-
:param aList: list
|
|
54
|
-
:type aList: python:list
|
|
55
|
-
:param popElement: element to pop
|
|
56
|
-
"""
|
|
57
|
-
if popElement in aList:
|
|
58
|
-
return aList.pop(aList.index(popElement))
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def stringListToString(aList: list) -> str:
|
|
62
|
-
"""This function is used for making MySQL queries with a list of string elements.
|
|
63
|
-
|
|
64
|
-
:param aList: list to be serialized to string for making queries
|
|
65
|
-
"""
|
|
66
|
-
return ",".join(f"'{x}'" for x in aList)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def intListToString(aList: list) -> str:
|
|
70
|
-
"""This function is used for making MySQL queries with a list of int elements.
|
|
71
|
-
|
|
72
|
-
:param aList: list to be serialized to string for making queries
|
|
73
|
-
"""
|
|
74
|
-
return ",".join(str(x) for x in aList)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def getChunk(aList: list, chunkSize: int):
|
|
78
|
-
"""Generator yielding chunk from a list of a size chunkSize.
|
|
79
|
-
|
|
80
|
-
:param aList: list to be splitted
|
|
81
|
-
:param chunkSize: lenght of one chunk
|
|
82
|
-
:raise: StopIteration
|
|
83
|
-
|
|
84
|
-
Usage:
|
|
85
|
-
|
|
86
|
-
>>> for chunk in getChunk( aList, chunkSize=10):
|
|
87
|
-
process( chunk )
|
|
88
|
-
|
|
89
|
-
"""
|
|
90
|
-
chunkSize = int(chunkSize)
|
|
91
|
-
for i in range(0, len(aList), chunkSize):
|
|
92
|
-
yield aList[i : i + chunkSize]
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
def breakListIntoChunks(aList: list, chunkSize: int):
|
|
96
|
-
"""This function takes a list as input and breaks it into list of size 'chunkSize'.
|
|
97
|
-
It returns a list of lists.
|
|
98
|
-
|
|
99
|
-
:param aList: list of elements
|
|
100
|
-
:param chunkSize: len of a single chunk
|
|
101
|
-
:return: list of lists of length of chunkSize
|
|
102
|
-
:raise: RuntimeError if numberOfFilesInChunk is less than 1
|
|
103
|
-
"""
|
|
104
|
-
if chunkSize < 1:
|
|
105
|
-
raise RuntimeError("chunkSize cannot be less than 1")
|
|
106
|
-
if isinstance(aList, (set, dict, tuple, {}.keys().__class__, {}.items().__class__, {}.values().__class__)):
|
|
107
|
-
aList = list(aList)
|
|
108
|
-
return [chunk for chunk in getChunk(aList, chunkSize)]
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
def getIndexInList(anItem: Any, aList: list) -> int:
|
|
112
|
-
"""Return the index of the element x in the list l
|
|
113
|
-
or sys.maxint if it does not exist
|
|
114
|
-
|
|
115
|
-
:param anItem: element to look for
|
|
116
|
-
:param aList: list to look into
|
|
117
|
-
|
|
118
|
-
:return: the index or sys.maxint
|
|
119
|
-
"""
|
|
120
|
-
# try:
|
|
121
|
-
if anItem in aList:
|
|
122
|
-
return aList.index(anItem)
|
|
123
|
-
else:
|
|
124
|
-
return sys.maxsize
|
|
1
|
+
from DIRACCommon.Core.Utilities.List import * # noqa: F401,F403
|
DIRAC/Core/Utilities/MySQL.py
CHANGED
|
@@ -1,151 +1,152 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
"""DIRAC Basic MySQL Class
|
|
2
|
+
It provides access to the basic MySQL methods in a multithread-safe mode
|
|
3
|
+
keeping used connections in a python Queue for further reuse.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
These are the coded methods:
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
__init__( host, user, passwd, name, [maxConnsInQueue=10] )
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
Initializes the Queue and tries to connect to the DB server,
|
|
11
|
+
using the _connect method.
|
|
12
|
+
"maxConnsInQueue" defines the size of the Queue of open connections
|
|
13
|
+
that are kept for reuse. It also defined the maximum number of open
|
|
14
|
+
connections available from the object.
|
|
15
|
+
maxConnsInQueue = 0 means unlimited and it is not supported.
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
_except( methodName, exception, errorMessage )
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
Helper method for exceptions: the "methodName" and the "errorMessage"
|
|
21
|
+
are printed with ERROR level, then the "exception" is printed (with
|
|
22
|
+
full description if it is a MySQL Exception) and S_ERROR is returned
|
|
23
|
+
with the errorMessage and the exception.
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
_connect()
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
Attempts connection to DB and sets the _connected flag to True upon success.
|
|
29
|
+
Returns S_OK or S_ERROR.
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
_query( cmd, [conn=conn] )
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
Executes SQL command "cmd".
|
|
35
|
+
Gets a connection from the Queue (or open a new one if none is available),
|
|
36
|
+
the used connection is back into the Queue.
|
|
37
|
+
If a connection to the the DB is passed as second argument this connection
|
|
38
|
+
is used and is not in the Queue.
|
|
39
|
+
Returns S_OK with fetchall() out in Value or S_ERROR upon failure.
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
_update( cmd, [conn=conn] )
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
Executes SQL command "cmd" and issue a commit
|
|
45
|
+
Gets a connection from the Queue (or open a new one if none is available),
|
|
46
|
+
the used connection is back into the Queue.
|
|
47
|
+
If a connection to the the DB is passed as second argument this connection
|
|
48
|
+
is used and is not in the Queue
|
|
49
|
+
Returns S_OK with number of updated registers in Value or S_ERROR upon failure.
|
|
50
50
|
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
_createTables( tableDict )
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
Create a new Table in the DB
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
_getConnection()
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
Gets a connection from the Queue (or open a new one if none is available)
|
|
60
|
+
Returns S_OK with connection in Value or S_ERROR
|
|
61
|
+
the calling method is responsible for closing this connection once it is no
|
|
62
|
+
longer needed.
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
Some high level methods have been added to avoid the need to write SQL
|
|
68
|
+
statement in most common cases. They should be used instead of low level
|
|
69
|
+
_insert, _update methods when ever possible.
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
buildCondition( self, condDict = None, older = None, newer = None,
|
|
72
|
+
timeStamp = None, orderAttribute = None, limit = False,
|
|
73
|
+
greater = None, smaller = None ):
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
75
|
+
Build SQL condition statement from provided condDict and other extra check on
|
|
76
|
+
a specified time stamp.
|
|
77
|
+
The conditions dictionary specifies for each attribute one or a List of possible
|
|
78
|
+
values
|
|
79
|
+
greater and smaller are dictionaries in which the keys are the names of the fields,
|
|
80
|
+
that are requested to be >= or < than the corresponding value.
|
|
81
|
+
For compatibility with current usage it uses Exceptions to exit in case of
|
|
82
|
+
invalid arguments
|
|
83
83
|
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
insertFields( self, tableName, inFields = None, inValues = None, conn = None, inDict = None ):
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
Insert a new row in "tableName" assigning the values "inValues" to the
|
|
88
|
+
fields "inFields".
|
|
89
|
+
Alternatively inDict can be used
|
|
90
|
+
String type values will be appropriately escaped.
|
|
91
91
|
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
93
|
+
updateFields( self, tableName, updateFields = None, updateValues = None,
|
|
94
|
+
condDict = None,
|
|
95
|
+
limit = False, conn = None,
|
|
96
|
+
updateDict = None,
|
|
97
|
+
older = None, newer = None,
|
|
98
|
+
timeStamp = None, orderAttribute = None ):
|
|
99
99
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
100
|
+
Update "updateFields" from "tableName" with "updateValues".
|
|
101
|
+
updateDict alternative way to provide the updateFields and updateValues
|
|
102
|
+
N records can match the condition
|
|
103
|
+
return S_OK( number of updated rows )
|
|
104
|
+
if limit is not False, the given limit is set
|
|
105
|
+
String type values will be appropriately escaped.
|
|
106
106
|
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
condDict = None,
|
|
110
|
-
limit = False, conn = None,
|
|
111
|
-
older = None, newer = None,
|
|
112
|
-
timeStamp = None, orderAttribute = None ):
|
|
113
|
-
|
|
114
|
-
Delete rows from "tableName" with
|
|
115
|
-
N records can match the condition
|
|
116
|
-
if limit is not False, the given limit is set
|
|
117
|
-
String type values will be appropriately escaped, they can be single values or lists of values.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
getFields( self, tableName, outFields = None,
|
|
108
|
+
deleteEntries( self, tableName,
|
|
121
109
|
condDict = None,
|
|
122
110
|
limit = False, conn = None,
|
|
123
111
|
older = None, newer = None,
|
|
124
112
|
timeStamp = None, orderAttribute = None ):
|
|
125
113
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
114
|
+
Delete rows from "tableName" with
|
|
115
|
+
N records can match the condition
|
|
116
|
+
if limit is not False, the given limit is set
|
|
117
|
+
String type values will be appropriately escaped, they can be single values or lists of values.
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
getFields( self, tableName, outFields = None,
|
|
121
|
+
condDict = None,
|
|
122
|
+
limit = False, conn = None,
|
|
123
|
+
older = None, newer = None,
|
|
124
|
+
timeStamp = None, orderAttribute = None ):
|
|
131
125
|
|
|
132
|
-
|
|
126
|
+
Select "outFields" from "tableName" with condDict
|
|
127
|
+
N records can match the condition
|
|
128
|
+
return S_OK( tuple(Field,Value) )
|
|
129
|
+
if limit is not False, the given limit is set
|
|
130
|
+
String type values will be appropriately escaped, they can be single values or lists of values.
|
|
133
131
|
|
|
132
|
+
for compatibility with other methods condDict keyed argument is added
|
|
134
133
|
|
|
135
|
-
getCounters( self, table, attrList, condDict = None, older = None,
|
|
136
|
-
newer = None, timeStamp = None, connection = False ):
|
|
137
134
|
|
|
138
|
-
|
|
139
|
-
|
|
135
|
+
getCounters( self, table, attrList, condDict = None, older = None,
|
|
136
|
+
newer = None, timeStamp = None, connection = False ):
|
|
140
137
|
|
|
138
|
+
Count the number of records on each distinct combination of AttrList, selected
|
|
139
|
+
with condition defined by condDict and time stamps
|
|
141
140
|
|
|
142
|
-
getDistinctAttributeValues( self, table, attribute, condDict = None, older = None,
|
|
143
|
-
newer = None, timeStamp = None, connection = False ):
|
|
144
141
|
|
|
145
|
-
|
|
142
|
+
getDistinctAttributeValues( self, table, attribute, condDict = None, older = None,
|
|
143
|
+
newer = None, timeStamp = None, connection = False ):
|
|
144
|
+
|
|
145
|
+
Get distinct values of a table attribute under specified conditions
|
|
146
146
|
|
|
147
147
|
|
|
148
148
|
"""
|
|
149
|
+
|
|
149
150
|
import collections
|
|
150
151
|
import functools
|
|
151
152
|
import json
|
|
@@ -760,6 +761,7 @@ class MySQL:
|
|
|
760
761
|
|
|
761
762
|
:return: S_OK with number of updated registers upon success.
|
|
762
763
|
S_ERROR upon error.
|
|
764
|
+
lastRowId: if set, added to the returned dictionary
|
|
763
765
|
"""
|
|
764
766
|
|
|
765
767
|
self.log.debug(f"_update: {self._safeCmd(cmd)}")
|
|
@@ -774,9 +776,11 @@ class MySQL:
|
|
|
774
776
|
try:
|
|
775
777
|
cursor = connection.cursor()
|
|
776
778
|
res = cursor.execute(cmd, args=args)
|
|
779
|
+
|
|
777
780
|
retDict = S_OK(res)
|
|
778
781
|
if cursor.lastrowid:
|
|
779
782
|
retDict["lastRowId"] = cursor.lastrowid
|
|
783
|
+
|
|
780
784
|
except Exception as x:
|
|
781
785
|
retDict = self._except("_update", x, "Execution failed.", cmd, debug)
|
|
782
786
|
|
|
@@ -929,7 +933,7 @@ class MySQL:
|
|
|
929
933
|
index is the list of fields to be indexed. This indexes will declared
|
|
930
934
|
unique.
|
|
931
935
|
"Engine": use the given DB engine, InnoDB is the default if not present.
|
|
932
|
-
"Charset": use the given character set. Default is
|
|
936
|
+
"Charset": use the given character set. Default is utf8mb4
|
|
933
937
|
force:
|
|
934
938
|
if True, requested tables are DROP if they exist.
|
|
935
939
|
if False, returned with S_ERROR if table exist.
|
|
@@ -1046,7 +1050,7 @@ class MySQL:
|
|
|
1046
1050
|
)
|
|
1047
1051
|
|
|
1048
1052
|
engine = thisTable.get("Engine", "InnoDB")
|
|
1049
|
-
charset = thisTable.get("Charset", "
|
|
1053
|
+
charset = thisTable.get("Charset", "utf8mb4")
|
|
1050
1054
|
|
|
1051
1055
|
cmd = "CREATE TABLE `{}` (\n{}\n) ENGINE={} DEFAULT CHARSET={}".format(
|
|
1052
1056
|
table,
|
DIRAC/Core/Utilities/Os.py
CHANGED
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
by default on Error they return None
|
|
4
4
|
"""
|
|
5
5
|
import os
|
|
6
|
+
import threading
|
|
6
7
|
|
|
7
8
|
import DIRAC
|
|
8
|
-
from DIRAC.Core.Utilities.Subprocess import shellCall, systemCall
|
|
9
9
|
from DIRAC.Core.Utilities import List
|
|
10
|
+
from DIRAC.Core.Utilities.Subprocess import shellCall, systemCall
|
|
10
11
|
|
|
11
12
|
DEBUG = 0
|
|
12
13
|
|
|
@@ -128,3 +129,33 @@ def sourceEnv(timeout, cmdTuple, inputEnv=None):
|
|
|
128
129
|
result["stderr"] = stderr
|
|
129
130
|
|
|
130
131
|
return result
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def safe_listdir(directory, timeout=60):
|
|
135
|
+
"""This is a "safe" list directory,
|
|
136
|
+
for lazily-loaded File Systems like CVMFS.
|
|
137
|
+
There's by default a 60 seconds timeout.
|
|
138
|
+
|
|
139
|
+
.. warning::
|
|
140
|
+
There is no distinction between an empty directory, and a non existent one.
|
|
141
|
+
It will return `[]` in both cases.
|
|
142
|
+
|
|
143
|
+
:param str directory: directory to list
|
|
144
|
+
:param int timeout: optional timeout, in seconds. Defaults to 60.
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
def listdir(directory):
|
|
148
|
+
try:
|
|
149
|
+
return os.listdir(directory)
|
|
150
|
+
except FileNotFoundError:
|
|
151
|
+
print(f"{directory} not found")
|
|
152
|
+
return []
|
|
153
|
+
|
|
154
|
+
contents = []
|
|
155
|
+
t = threading.Thread(target=lambda: contents.extend(listdir(directory)))
|
|
156
|
+
t.daemon = True # don't delay program's exit
|
|
157
|
+
t.start()
|
|
158
|
+
t.join(timeout)
|
|
159
|
+
if t.is_alive():
|
|
160
|
+
return None # timeout
|
|
161
|
+
return contents
|
DIRAC/Core/Utilities/Platform.py
CHANGED
|
@@ -2,119 +2,14 @@
|
|
|
2
2
|
Compile the externals
|
|
3
3
|
"""
|
|
4
4
|
import platform
|
|
5
|
-
import sys
|
|
6
|
-
import os
|
|
7
|
-
import re
|
|
8
|
-
|
|
9
|
-
# We need to patch python platform module. It does a string comparison for the libc versions.
|
|
10
|
-
# it fails when going from 2.9 to 2.10,
|
|
11
|
-
# the fix converts the version to a tuple and attempts a numeric comparison
|
|
12
|
-
|
|
13
|
-
_libc_search = re.compile(b"(__libc_init)" b"|" b"(GLIBC_([0-9.]+))" b"|" b"(libc(_\\w+)?\\.so(?:\\.(\\d[0-9.]*))?)")
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def libc_ver(executable=sys.executable, lib="", version="", chunksize=2048):
|
|
17
|
-
"""Tries to determine the libc version that the file executable
|
|
18
|
-
(which defaults to the Python interpreter) is linked against.
|
|
19
|
-
|
|
20
|
-
Returns a tuple of strings (lib,version) which default to the
|
|
21
|
-
given parameters in case the lookup fails.
|
|
22
|
-
|
|
23
|
-
Note that the function has intimate knowledge of how different
|
|
24
|
-
libc versions add symbols to the executable and thus is probably
|
|
25
|
-
only useable for executables compiled using gcc.
|
|
26
|
-
|
|
27
|
-
The file is read and scanned in chunks of chunksize bytes.
|
|
28
|
-
|
|
29
|
-
"""
|
|
30
|
-
with open(executable, "rb") as f:
|
|
31
|
-
binary = f.read(chunksize)
|
|
32
|
-
pos = 0
|
|
33
|
-
version = [0, 0, 0]
|
|
34
|
-
while True:
|
|
35
|
-
m = _libc_search.search(binary, pos)
|
|
36
|
-
if not m:
|
|
37
|
-
binary = f.read(chunksize)
|
|
38
|
-
if not binary:
|
|
39
|
-
break
|
|
40
|
-
pos = 0
|
|
41
|
-
continue
|
|
42
|
-
libcinit, glibc, glibcversion, so, threads, soversion = m.groups()
|
|
43
|
-
if libcinit and not lib:
|
|
44
|
-
lib = b"libc"
|
|
45
|
-
elif glibc:
|
|
46
|
-
glibcversion_parts = glibcversion.split(b".")
|
|
47
|
-
for i, part in enumerate(glibcversion_parts):
|
|
48
|
-
try:
|
|
49
|
-
glibcversion_parts[i] = int(part)
|
|
50
|
-
except ValueError:
|
|
51
|
-
glibcversion_parts[i] = 0
|
|
52
|
-
if libcinit and not lib:
|
|
53
|
-
lib = b"libc"
|
|
54
|
-
elif glibc:
|
|
55
|
-
if lib != b"glibc":
|
|
56
|
-
lib = b"glibc"
|
|
57
|
-
version = glibcversion_parts
|
|
58
|
-
elif glibcversion_parts > version:
|
|
59
|
-
version = glibcversion_parts
|
|
60
|
-
elif so:
|
|
61
|
-
if lib != b"glibc":
|
|
62
|
-
lib = b"libc"
|
|
63
|
-
version = max(version, [int(soversion)]).pop()
|
|
64
|
-
if threads and version[-len(threads) :] != threads:
|
|
65
|
-
version = version + threads
|
|
66
|
-
pos = m.end()
|
|
67
|
-
return lib.decode(), ".".join(map(str, version))
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
# ## Command line interface
|
|
71
5
|
|
|
72
6
|
|
|
7
|
+
# Command line interface
|
|
73
8
|
def getPlatformString():
|
|
74
9
|
# Modified to return our desired platform string, R. Graciani
|
|
75
10
|
platformTuple = (platform.system(), platform.machine())
|
|
76
11
|
if platformTuple[0] == "Linux":
|
|
77
|
-
|
|
78
|
-
import subprocess
|
|
79
|
-
|
|
80
|
-
sp = subprocess.Popen(["/sbin/ldconfig", "--print-cache"], stdout=subprocess.PIPE, universal_newlines=True)
|
|
81
|
-
spStdout = sp.stdout
|
|
82
|
-
except Exception:
|
|
83
|
-
sp = None
|
|
84
|
-
spStdout = os.popen("/sbin/ldconfig --print-cache", "r")
|
|
85
|
-
ldre = re.compile(r".*=> (.*/libc\.so\..*$)")
|
|
86
|
-
libs = []
|
|
87
|
-
for line in spStdout.readlines():
|
|
88
|
-
reM = ldre.match(line)
|
|
89
|
-
if reM:
|
|
90
|
-
libs.append(reM.groups()[0])
|
|
91
|
-
if sp:
|
|
92
|
-
if "terminate" in dir(sp):
|
|
93
|
-
sp.terminate()
|
|
94
|
-
sp.wait()
|
|
95
|
-
|
|
96
|
-
if not libs:
|
|
97
|
-
# get version of higher libc installed
|
|
98
|
-
if platform.machine().find("64") != -1:
|
|
99
|
-
lib = "/lib64"
|
|
100
|
-
else:
|
|
101
|
-
lib = "/lib"
|
|
102
|
-
for libFile in os.listdir(lib):
|
|
103
|
-
if libFile.find("libc-") == 0 or libFile.find("libc.so") == 0:
|
|
104
|
-
libs.append(os.path.join(lib, libFile))
|
|
105
|
-
newest_lib = [0, 0, 0]
|
|
106
|
-
for lib in libs:
|
|
107
|
-
lib_parts = libc_ver(lib)[1].split(".")
|
|
108
|
-
for i, part in enumerate(lib_parts):
|
|
109
|
-
try:
|
|
110
|
-
lib_parts[i] = int(part)
|
|
111
|
-
except ValueError:
|
|
112
|
-
lib_parts[i] = 0
|
|
113
|
-
# print "non integer version numbers"
|
|
114
|
-
if lib_parts > newest_lib:
|
|
115
|
-
newest_lib = lib_parts
|
|
116
|
-
|
|
117
|
-
platformTuple += ("glibc-" + ".".join(map(str, newest_lib)),)
|
|
12
|
+
platformTuple += ("-".join(platform.libc_ver()),)
|
|
118
13
|
elif platformTuple[0] == "Darwin":
|
|
119
14
|
platformTuple += (".".join(platform.mac_ver()[0].split(".")[:2]),)
|
|
120
15
|
else:
|
DIRAC/Core/Utilities/Proxy.py
CHANGED
|
@@ -160,10 +160,6 @@ def executeWithoutServerCertificate(fcn):
|
|
|
160
160
|
|
|
161
161
|
try:
|
|
162
162
|
return fcn(*args, **kwargs)
|
|
163
|
-
except Exception as lException: # pylint: disable=broad-except
|
|
164
|
-
value = ",".join([str(arg) for arg in lException.args])
|
|
165
|
-
exceptType = lException.__class__.__name__
|
|
166
|
-
return S_ERROR(f"Exception - {exceptType}: {value}")
|
|
167
163
|
finally:
|
|
168
164
|
# Restore the default host certificate usage if necessary
|
|
169
165
|
if useServerCertificate:
|