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.
Files changed (236) hide show
  1. DIRAC/AccountingSystem/Client/AccountingCLI.py +0 -140
  2. DIRAC/AccountingSystem/Client/DataStoreClient.py +0 -13
  3. DIRAC/AccountingSystem/Client/Types/BaseAccountingType.py +0 -7
  4. DIRAC/AccountingSystem/ConfigTemplate.cfg +0 -5
  5. DIRAC/AccountingSystem/Service/DataStoreHandler.py +0 -72
  6. DIRAC/ConfigurationSystem/Client/Helpers/CSGlobals.py +0 -9
  7. DIRAC/ConfigurationSystem/Client/Helpers/Registry.py +38 -26
  8. DIRAC/ConfigurationSystem/Client/Helpers/Resources.py +11 -43
  9. DIRAC/ConfigurationSystem/Client/Helpers/test/Test_Helpers.py +0 -16
  10. DIRAC/ConfigurationSystem/Client/LocalConfiguration.py +14 -8
  11. DIRAC/ConfigurationSystem/Client/PathFinder.py +47 -8
  12. DIRAC/ConfigurationSystem/Client/SyncPlugins/CERNLDAPSyncPlugin.py +4 -1
  13. DIRAC/ConfigurationSystem/Client/VOMS2CSSynchronizer.py +32 -19
  14. DIRAC/ConfigurationSystem/Client/test/Test_PathFinder.py +41 -1
  15. DIRAC/ConfigurationSystem/private/RefresherBase.py +4 -2
  16. DIRAC/Core/Base/API.py +4 -7
  17. DIRAC/Core/Base/SQLAlchemyDB.py +1 -0
  18. DIRAC/Core/DISET/ServiceReactor.py +11 -3
  19. DIRAC/Core/DISET/private/BaseClient.py +1 -2
  20. DIRAC/Core/DISET/private/Transports/M2SSLTransport.py +9 -7
  21. DIRAC/Core/DISET/private/Transports/SSL/M2Utils.py +3 -1
  22. DIRAC/Core/LCG/GOCDBClient.py +5 -7
  23. DIRAC/Core/Security/DiracX.py +31 -17
  24. DIRAC/Core/Security/IAMService.py +5 -10
  25. DIRAC/Core/Security/Locations.py +27 -18
  26. DIRAC/Core/Security/ProxyInfo.py +9 -5
  27. DIRAC/Core/Security/VOMSService.py +2 -4
  28. DIRAC/Core/Security/m2crypto/X509Certificate.py +4 -6
  29. DIRAC/Core/Security/m2crypto/asn1_utils.py +17 -5
  30. DIRAC/Core/Security/test/test_diracx_token_from_pem.py +161 -0
  31. DIRAC/Core/Tornado/Client/ClientSelector.py +4 -1
  32. DIRAC/Core/Tornado/Server/TornadoService.py +1 -1
  33. DIRAC/Core/Utilities/CGroups2.py +328 -0
  34. DIRAC/Core/Utilities/ClassAd/ClassAdLight.py +4 -290
  35. DIRAC/Core/Utilities/DErrno.py +5 -309
  36. DIRAC/Core/Utilities/Extensions.py +10 -1
  37. DIRAC/Core/Utilities/File.py +1 -1
  38. DIRAC/Core/Utilities/Graphs/GraphData.py +1 -1
  39. DIRAC/Core/Utilities/Graphs/GraphUtilities.py +6 -1
  40. DIRAC/Core/Utilities/JDL.py +1 -195
  41. DIRAC/Core/Utilities/List.py +1 -124
  42. DIRAC/Core/Utilities/MySQL.py +103 -99
  43. DIRAC/Core/Utilities/Os.py +32 -1
  44. DIRAC/Core/Utilities/Platform.py +2 -107
  45. DIRAC/Core/Utilities/Proxy.py +0 -4
  46. DIRAC/Core/Utilities/ReturnValues.py +7 -252
  47. DIRAC/Core/Utilities/StateMachine.py +12 -178
  48. DIRAC/Core/Utilities/Subprocess.py +35 -14
  49. DIRAC/Core/Utilities/TimeUtilities.py +10 -253
  50. DIRAC/Core/Utilities/test/Test_JDL.py +0 -3
  51. DIRAC/Core/Utilities/test/Test_Profiler.py +20 -20
  52. DIRAC/Core/scripts/dirac_agent.py +1 -1
  53. DIRAC/Core/scripts/dirac_apptainer_exec.py +72 -46
  54. DIRAC/Core/scripts/dirac_configure.py +1 -3
  55. DIRAC/Core/scripts/dirac_install_db.py +24 -6
  56. DIRAC/Core/scripts/dirac_platform.py +1 -92
  57. DIRAC/DataManagementSystem/Agent/FTS3Agent.py +8 -7
  58. DIRAC/DataManagementSystem/Agent/RequestOperations/RemoveFile.py +7 -6
  59. DIRAC/DataManagementSystem/Client/FTS3Job.py +71 -34
  60. DIRAC/DataManagementSystem/DB/FTS3DB.py +7 -3
  61. DIRAC/DataManagementSystem/DB/FileCatalogComponents/DatasetManager/DatasetManager.py +1 -1
  62. DIRAC/DataManagementSystem/DB/FileCatalogDB.sql +9 -9
  63. DIRAC/DataManagementSystem/DB/FileCatalogWithFkAndPsDB.sql +9 -9
  64. DIRAC/DataManagementSystem/Utilities/DMSHelpers.py +6 -2
  65. DIRAC/DataManagementSystem/scripts/dirac_admin_allow_se.py +13 -8
  66. DIRAC/DataManagementSystem/scripts/dirac_admin_ban_se.py +13 -8
  67. DIRAC/DataManagementSystem/scripts/dirac_dms_create_moving_request.py +2 -0
  68. DIRAC/DataManagementSystem/scripts/dirac_dms_protocol_matrix.py +0 -1
  69. DIRAC/FrameworkSystem/Client/BundleDeliveryClient.py +2 -7
  70. DIRAC/FrameworkSystem/Client/ComponentInstaller.py +9 -4
  71. DIRAC/FrameworkSystem/Client/ProxyManagerClient.py +5 -2
  72. DIRAC/FrameworkSystem/Client/SystemAdministratorClientCLI.py +11 -6
  73. DIRAC/FrameworkSystem/ConfigTemplate.cfg +2 -0
  74. DIRAC/FrameworkSystem/DB/AuthDB.py +3 -3
  75. DIRAC/FrameworkSystem/DB/InstalledComponentsDB.py +4 -4
  76. DIRAC/FrameworkSystem/DB/ProxyDB.py +11 -3
  77. DIRAC/FrameworkSystem/DB/TokenDB.py +1 -1
  78. DIRAC/FrameworkSystem/Service/ProxyManagerHandler.py +8 -6
  79. DIRAC/FrameworkSystem/Utilities/MonitoringUtilities.py +2 -19
  80. DIRAC/FrameworkSystem/Utilities/TokenManagementUtilities.py +3 -2
  81. DIRAC/FrameworkSystem/Utilities/diracx.py +36 -14
  82. DIRAC/FrameworkSystem/private/authorization/AuthServer.py +2 -2
  83. DIRAC/FrameworkSystem/scripts/dirac_admin_update_pilot.py +18 -11
  84. DIRAC/FrameworkSystem/scripts/dirac_login.py +2 -2
  85. DIRAC/FrameworkSystem/scripts/dirac_proxy_init.py +7 -8
  86. DIRAC/Interfaces/API/Dirac.py +27 -15
  87. DIRAC/Interfaces/API/DiracAdmin.py +45 -17
  88. DIRAC/Interfaces/API/Job.py +9 -13
  89. DIRAC/Interfaces/scripts/dirac_admin_allow_site.py +12 -18
  90. DIRAC/Interfaces/scripts/dirac_admin_ban_site.py +12 -10
  91. DIRAC/Interfaces/scripts/dirac_admin_get_site_mask.py +4 -13
  92. DIRAC/Interfaces/scripts/dirac_admin_reset_job.py +3 -6
  93. DIRAC/Interfaces/scripts/dirac_wms_job_parameters.py +0 -1
  94. DIRAC/MonitoringSystem/Client/Types/WMSHistory.py +4 -0
  95. DIRAC/MonitoringSystem/Client/WebAppClient.py +26 -0
  96. DIRAC/MonitoringSystem/ConfigTemplate.cfg +9 -0
  97. DIRAC/MonitoringSystem/DB/MonitoringDB.py +6 -25
  98. DIRAC/MonitoringSystem/Service/MonitoringHandler.py +0 -33
  99. DIRAC/MonitoringSystem/Service/WebAppHandler.py +599 -0
  100. DIRAC/MonitoringSystem/private/MainReporter.py +0 -3
  101. DIRAC/ProductionSystem/DB/ProductionDB.sql +4 -4
  102. DIRAC/ProductionSystem/scripts/dirac_prod_get.py +2 -2
  103. DIRAC/ProductionSystem/scripts/dirac_prod_get_all.py +2 -2
  104. DIRAC/ProductionSystem/scripts/dirac_prod_get_trans.py +2 -3
  105. DIRAC/RequestManagementSystem/Agent/RequestExecutingAgent.py +8 -6
  106. DIRAC/RequestManagementSystem/Agent/RequestOperations/ForwardDISET.py +2 -14
  107. DIRAC/RequestManagementSystem/Client/ReqClient.py +66 -13
  108. DIRAC/RequestManagementSystem/ConfigTemplate.cfg +6 -6
  109. DIRAC/RequestManagementSystem/DB/RequestDB.py +10 -5
  110. DIRAC/RequestManagementSystem/DB/test/RMSTestScenari.py +2 -0
  111. DIRAC/RequestManagementSystem/private/RequestValidator.py +40 -46
  112. DIRAC/ResourceStatusSystem/Client/SiteStatus.py +4 -2
  113. DIRAC/ResourceStatusSystem/Command/FreeDiskSpaceCommand.py +3 -1
  114. DIRAC/ResourceStatusSystem/DB/ResourceManagementDB.py +8 -8
  115. DIRAC/ResourceStatusSystem/DB/ResourceStatusDB.py +2 -2
  116. DIRAC/ResourceStatusSystem/Utilities/CSHelpers.py +2 -31
  117. DIRAC/ResourceStatusSystem/scripts/dirac_rss_set_status.py +30 -12
  118. DIRAC/Resources/Catalog/RucioFileCatalogClient.py +195 -1
  119. DIRAC/Resources/Catalog/test/Test_RucioFileCatalogClient.py +181 -0
  120. DIRAC/Resources/Computing/AREXComputingElement.py +25 -8
  121. DIRAC/Resources/Computing/BatchSystems/Condor.py +126 -108
  122. DIRAC/Resources/Computing/BatchSystems/SLURM.py +5 -1
  123. DIRAC/Resources/Computing/BatchSystems/test/Test_SLURM.py +46 -0
  124. DIRAC/Resources/Computing/ComputingElement.py +1 -1
  125. DIRAC/Resources/Computing/HTCondorCEComputingElement.py +44 -44
  126. DIRAC/Resources/Computing/InProcessComputingElement.py +4 -2
  127. DIRAC/Resources/Computing/LocalComputingElement.py +1 -18
  128. DIRAC/Resources/Computing/SSHBatchComputingElement.py +1 -17
  129. DIRAC/Resources/Computing/SSHComputingElement.py +1 -18
  130. DIRAC/Resources/Computing/SingularityComputingElement.py +19 -5
  131. DIRAC/Resources/Computing/test/Test_HTCondorCEComputingElement.py +67 -49
  132. DIRAC/Resources/Computing/test/Test_PoolComputingElement.py +2 -1
  133. DIRAC/Resources/IdProvider/CheckInIdProvider.py +13 -0
  134. DIRAC/Resources/IdProvider/IdProviderFactory.py +11 -3
  135. DIRAC/Resources/MessageQueue/StompMQConnector.py +1 -1
  136. DIRAC/Resources/Storage/GFAL2_StorageBase.py +24 -15
  137. DIRAC/Resources/Storage/OccupancyPlugins/WLCGAccountingHTTPJson.py +1 -3
  138. DIRAC/Resources/Storage/StorageBase.py +4 -2
  139. DIRAC/Resources/Storage/StorageElement.py +6 -7
  140. DIRAC/StorageManagementSystem/DB/StorageManagementDB.sql +2 -2
  141. DIRAC/TransformationSystem/Agent/TaskManagerAgentBase.py +10 -16
  142. DIRAC/TransformationSystem/Agent/TransformationAgent.py +22 -1
  143. DIRAC/TransformationSystem/Agent/TransformationCleaningAgent.py +16 -16
  144. DIRAC/TransformationSystem/Client/TaskManager.py +2 -4
  145. DIRAC/TransformationSystem/Client/Transformation.py +6 -7
  146. DIRAC/TransformationSystem/Client/TransformationClient.py +21 -11
  147. DIRAC/TransformationSystem/Client/Utilities.py +9 -0
  148. DIRAC/TransformationSystem/DB/TransformationDB.py +11 -14
  149. DIRAC/TransformationSystem/DB/TransformationDB.sql +9 -9
  150. DIRAC/TransformationSystem/Service/TransformationManagerHandler.py +0 -333
  151. DIRAC/TransformationSystem/Utilities/ReplicationCLIParameters.py +3 -3
  152. DIRAC/TransformationSystem/Utilities/TransformationInfo.py +7 -5
  153. DIRAC/TransformationSystem/scripts/dirac_production_runjoblocal.py +2 -4
  154. DIRAC/TransformationSystem/test/Test_TransformationInfo.py +22 -15
  155. DIRAC/TransformationSystem/test/Test_replicationTransformation.py +5 -6
  156. DIRAC/Workflow/Modules/test/Test_Modules.py +5 -0
  157. DIRAC/WorkloadManagementSystem/Agent/JobAgent.py +38 -26
  158. DIRAC/WorkloadManagementSystem/Agent/JobCleaningAgent.py +12 -8
  159. DIRAC/WorkloadManagementSystem/Agent/PilotSyncAgent.py +4 -3
  160. DIRAC/WorkloadManagementSystem/Agent/PushJobAgent.py +13 -13
  161. DIRAC/WorkloadManagementSystem/Agent/SiteDirector.py +18 -14
  162. DIRAC/WorkloadManagementSystem/Agent/StalledJobAgent.py +18 -51
  163. DIRAC/WorkloadManagementSystem/Agent/StatesAccountingAgent.py +41 -1
  164. DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_JobAgent.py +45 -4
  165. DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_JobCleaningAgent.py +7 -9
  166. DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_PushJobAgent.py +1 -0
  167. DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_SiteDirector.py +9 -2
  168. DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_StalledJobAgent.py +4 -5
  169. DIRAC/WorkloadManagementSystem/Client/DownloadInputData.py +9 -9
  170. DIRAC/WorkloadManagementSystem/Client/InputDataResolution.py +6 -6
  171. DIRAC/WorkloadManagementSystem/Client/JobMonitoringClient.py +10 -11
  172. DIRAC/WorkloadManagementSystem/Client/JobReport.py +1 -1
  173. DIRAC/WorkloadManagementSystem/Client/JobState/CachedJobState.py +3 -0
  174. DIRAC/WorkloadManagementSystem/Client/JobState/JobManifest.py +32 -261
  175. DIRAC/WorkloadManagementSystem/Client/JobState/JobState.py +6 -0
  176. DIRAC/WorkloadManagementSystem/Client/JobStateUpdateClient.py +3 -0
  177. DIRAC/WorkloadManagementSystem/Client/JobStatus.py +8 -152
  178. DIRAC/WorkloadManagementSystem/Client/PoolXMLSlice.py +12 -19
  179. DIRAC/WorkloadManagementSystem/Client/SandboxStoreClient.py +25 -38
  180. DIRAC/WorkloadManagementSystem/Client/WMSClient.py +2 -3
  181. DIRAC/WorkloadManagementSystem/Client/test/Test_Client_DownloadInputData.py +29 -0
  182. DIRAC/WorkloadManagementSystem/ConfigTemplate.cfg +4 -8
  183. DIRAC/WorkloadManagementSystem/DB/JobDB.py +89 -132
  184. DIRAC/WorkloadManagementSystem/DB/JobDB.sql +8 -8
  185. DIRAC/WorkloadManagementSystem/DB/JobDBUtils.py +18 -147
  186. DIRAC/WorkloadManagementSystem/DB/JobLoggingDB.py +19 -6
  187. DIRAC/WorkloadManagementSystem/DB/JobParametersDB.py +9 -9
  188. DIRAC/WorkloadManagementSystem/DB/PilotAgentsDB.py +16 -5
  189. DIRAC/WorkloadManagementSystem/DB/PilotAgentsDB.sql +3 -3
  190. DIRAC/WorkloadManagementSystem/DB/SandboxMetadataDB.py +44 -82
  191. DIRAC/WorkloadManagementSystem/DB/StatusUtils.py +125 -0
  192. DIRAC/WorkloadManagementSystem/DB/tests/Test_JobDB.py +1 -1
  193. DIRAC/WorkloadManagementSystem/DB/tests/Test_StatusUtils.py +28 -0
  194. DIRAC/WorkloadManagementSystem/Executor/JobSanity.py +5 -4
  195. DIRAC/WorkloadManagementSystem/Executor/JobScheduling.py +4 -0
  196. DIRAC/WorkloadManagementSystem/FutureClient/JobStateUpdateClient.py +75 -33
  197. DIRAC/WorkloadManagementSystem/JobWrapper/JobWrapper.py +22 -11
  198. DIRAC/WorkloadManagementSystem/JobWrapper/JobWrapperTemplate.py +9 -10
  199. DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapper.py +60 -10
  200. DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapperTemplate.py +4 -0
  201. DIRAC/WorkloadManagementSystem/Service/JobManagerHandler.py +33 -154
  202. DIRAC/WorkloadManagementSystem/Service/JobMonitoringHandler.py +5 -323
  203. DIRAC/WorkloadManagementSystem/Service/JobStateUpdateHandler.py +0 -16
  204. DIRAC/WorkloadManagementSystem/Service/PilotManagerHandler.py +6 -103
  205. DIRAC/WorkloadManagementSystem/Service/SandboxStoreHandler.py +7 -53
  206. DIRAC/WorkloadManagementSystem/Service/WMSAdministratorHandler.py +16 -79
  207. DIRAC/WorkloadManagementSystem/Service/WMSUtilities.py +4 -18
  208. DIRAC/WorkloadManagementSystem/Utilities/JobModel.py +28 -209
  209. DIRAC/WorkloadManagementSystem/Utilities/JobParameters.py +65 -3
  210. DIRAC/WorkloadManagementSystem/Utilities/JobStatusUtility.py +2 -64
  211. DIRAC/WorkloadManagementSystem/Utilities/ParametricJob.py +7 -171
  212. DIRAC/WorkloadManagementSystem/Utilities/PilotCStoJSONSynchronizer.py +73 -7
  213. DIRAC/WorkloadManagementSystem/Utilities/PilotWrapper.py +41 -11
  214. DIRAC/WorkloadManagementSystem/Utilities/RemoteRunner.py +16 -0
  215. DIRAC/WorkloadManagementSystem/Utilities/Utils.py +36 -1
  216. DIRAC/WorkloadManagementSystem/Utilities/jobAdministration.py +15 -0
  217. DIRAC/WorkloadManagementSystem/Utilities/test/Test_JobModel.py +1 -15
  218. DIRAC/WorkloadManagementSystem/Utilities/test/Test_ParametricJob.py +45 -128
  219. DIRAC/WorkloadManagementSystem/Utilities/test/Test_PilotWrapper.py +16 -0
  220. DIRAC/WorkloadManagementSystem/scripts/dirac_jobexec.py +7 -2
  221. DIRAC/WorkloadManagementSystem/scripts/dirac_wms_pilot_job_info.py +1 -1
  222. DIRAC/__init__.py +62 -60
  223. DIRAC/tests/Utilities/testJobDefinitions.py +22 -28
  224. {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info}/METADATA +8 -5
  225. {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info}/RECORD +229 -228
  226. {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info}/WHEEL +1 -1
  227. {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info}/entry_points.txt +0 -3
  228. DIRAC/Core/Utilities/test/Test_List.py +0 -150
  229. DIRAC/Core/Utilities/test/Test_Time.py +0 -88
  230. DIRAC/Resources/Computing/PilotBundle.py +0 -70
  231. DIRAC/TransformationSystem/scripts/dirac_transformation_archive.py +0 -30
  232. DIRAC/TransformationSystem/scripts/dirac_transformation_clean.py +0 -30
  233. DIRAC/TransformationSystem/scripts/dirac_transformation_remove_output.py +0 -30
  234. DIRAC/WorkloadManagementSystem/Utilities/test/Test_JobManager.py +0 -58
  235. {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info/licenses}/LICENSE +0 -0
  236. {DIRAC-9.0.0a42.dist-info → dirac-9.0.7.dist-info}/top_level.txt +0 -0
@@ -1,260 +1,17 @@
1
- """
2
- DIRAC TimeUtilities module
3
- Support for basic Date and Time operations
4
- based on system datetime module.
5
-
6
- It provides common interface to UTC timestamps,
7
- converter to string types and back.
8
-
9
- Useful timedelta constant are also provided to
10
- define time intervals.
11
-
12
- Notice: datetime.timedelta objects allow multiplication and division by interger
13
- but not by float. Thus:
14
-
15
- - DIRAC.TimeUtilities.second * 1.5 is not allowed
16
- - DIRAC.TimeUtilities.second * 3 / 2 is allowed
1
+ """Backward compatibility wrapper - moved to DIRACCommon
17
2
 
18
- An timeInterval class provides a method to check
19
- if a give datetime is in the defined interval.
3
+ This module has been moved to DIRACCommon.Core.Utilities.TimeUtilities to avoid
4
+ circular dependencies and allow DiracX to use these utilities without
5
+ triggering DIRAC's global state initialization.
20
6
 
7
+ All exports are maintained for backward compatibility.
21
8
  """
22
- import datetime
23
- import sys
24
- import time
25
-
26
- from DIRAC import gLogger
27
-
28
- # Some useful constants for time operations
29
- microsecond = datetime.timedelta(microseconds=1)
30
- second = datetime.timedelta(seconds=1)
31
- minute = datetime.timedelta(minutes=1)
32
- hour = datetime.timedelta(hours=1)
33
- day = datetime.timedelta(days=1)
34
- week = datetime.timedelta(days=7)
35
-
36
-
37
- def timeThis(method):
38
- """Function to be used as a decorator for timing other functions/methods"""
39
-
40
- def timed(*args, **kw):
41
- """What actually times"""
42
- ts = time.time()
43
- result = method(*args, **kw)
44
- if sys.stdout.isatty():
45
- return result
46
- te = time.time()
47
-
48
- pre = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC ")
49
-
50
- try:
51
- pre += args[0].log.getName() + "/" + args[0].log.getSubName() + " TIME: " + args[0].transString
52
- except AttributeError:
53
- try:
54
- pre += args[0].log.getName() + " TIME: " + args[0].transString
55
- except AttributeError:
56
- try:
57
- pre += args[0].log.getName() + "/" + args[0].log.getSubName() + " TIME: "
58
- except AttributeError:
59
- pre += "TIME: "
60
- except IndexError:
61
- pre += "TIME: "
62
-
63
- argsLen = ""
64
- if args:
65
- try:
66
- if isinstance(args[1], (list, dict)):
67
- argsLen = f"arguments len: {len(args[1])}"
68
- except IndexError:
69
- if kw:
70
- try:
71
- if isinstance(list(list(kw.items())[0])[1], (list, dict)):
72
- argsLen = f"arguments len: {len(list(list(kw.items())[0])[1])}"
73
- except IndexError:
74
- argsLen = ""
75
-
76
- gLogger.info(f"{pre} Exec time ===> function {method.__name__!r} {argsLen} -> {te - ts:2.2f} sec")
77
- return result
78
-
79
- return timed
80
-
81
-
82
- def toEpoch(dateTimeObject=None):
83
- """
84
- Get seconds since epoch. Accepts datetime or date objects
85
- """
86
- return toEpochMilliSeconds(dateTimeObject) // 1000
9
+ from functools import partial
87
10
 
11
+ # Re-export everything from DIRACCommon for backward compatibility
12
+ from DIRACCommon.Core.Utilities.TimeUtilities import * # noqa: F401, F403
88
13
 
89
- def toEpochMilliSeconds(dateTimeObject=None):
90
- """
91
- Get milliseconds since epoch
92
- """
93
- if dateTimeObject is None:
94
- dateTimeObject = datetime.datetime.utcnow()
95
- if dateTimeObject.resolution == datetime.timedelta(days=1):
96
- # Add time information corresponding to midnight UTC if it's a datetime.date
97
- dateTimeObject = datetime.datetime.combine(
98
- dateTimeObject, datetime.time.min.replace(tzinfo=datetime.timezone.utc)
99
- )
100
- posixTime = dateTimeObject.replace(tzinfo=datetime.timezone.utc).timestamp()
101
- return int(posixTime * 1000)
102
-
103
-
104
- def fromEpoch(epoch):
105
- """
106
- Get datetime object from epoch
107
- """
108
- # Check if the timestamp is in milliseconds
109
- if epoch > 10**17: # nanoseconds
110
- epoch /= 1000**3
111
- elif epoch > 10**14: # microseconds
112
- epoch /= 1000**2
113
- elif epoch > 10**11: # milliseconds
114
- epoch /= 1000
115
- return datetime.datetime.utcfromtimestamp(epoch)
116
-
117
-
118
- def toString(myDate=None):
119
- """
120
- Convert to String
121
- if argument type is neither _dateTimeType, _dateType, nor _timeType
122
- the current dateTime converted to String is returned instead
123
-
124
- Notice: datetime.timedelta are converted to strings using the format:
125
- [day] days [hour]:[min]:[sec]:[microsec]
126
- where hour, min, sec, microsec are always positive integers,
127
- and day carries the sign.
128
- To keep internal consistency we are using:
129
- [hour]:[min]:[sec]:[microsec]
130
- where min, sec, microsec are always positive integers and hour carries the sign.
131
- """
132
- if isinstance(myDate, datetime.date):
133
- return str(myDate)
134
-
135
- elif isinstance(myDate, datetime.time):
136
- return "%02d:%02d:%02d.%06d" % (
137
- myDate.days * 24 + myDate.seconds / 3600,
138
- myDate.seconds % 3600 / 60,
139
- myDate.seconds % 60,
140
- myDate.microseconds,
141
- )
142
- else:
143
- return toString(datetime.datetime.utcnow())
144
-
145
-
146
- def fromString(myDate=None):
147
- """
148
- Convert date/time/datetime String back to appropriated objects
149
-
150
- The format of the string it is assume to be that returned by toString method.
151
- See notice on toString method
152
- On Error, return None
153
-
154
- :param myDate: the date string to be converted
155
- :type myDate: str or datetime.datetime
156
- """
157
- if isinstance(myDate, datetime.datetime):
158
- return myDate
159
- if isinstance(myDate, str):
160
- if myDate.find(" ") > 0:
161
- dateTimeTuple = myDate.split(" ")
162
- dateTuple = dateTimeTuple[0].split("-")
163
- try:
164
- return datetime.datetime(year=dateTuple[0], month=dateTuple[1], day=dateTuple[2]) + fromString(
165
- dateTimeTuple[1]
166
- )
167
- # return datetime.datetime.utcnow().combine( fromString( dateTimeTuple[0] ),
168
- # fromString( dateTimeTuple[1] ) )
169
- except Exception:
170
- try:
171
- return datetime.datetime(
172
- year=int(dateTuple[0]), month=int(dateTuple[1]), day=int(dateTuple[2])
173
- ) + fromString(dateTimeTuple[1])
174
- except ValueError:
175
- return None
176
- # return datetime.datetime.utcnow().combine( fromString( dateTimeTuple[0] ),
177
- # fromString( dateTimeTuple[1] ) )
178
- elif myDate.find(":") > 0:
179
- timeTuple = myDate.replace(".", ":").split(":")
180
- try:
181
- if len(timeTuple) == 4:
182
- return datetime.timedelta(
183
- hours=int(timeTuple[0]),
184
- minutes=int(timeTuple[1]),
185
- seconds=int(timeTuple[2]),
186
- microseconds=int(timeTuple[3]),
187
- )
188
- elif len(timeTuple) == 3:
189
- try:
190
- return datetime.timedelta(
191
- hours=int(timeTuple[0]),
192
- minutes=int(timeTuple[1]),
193
- seconds=int(timeTuple[2]),
194
- microseconds=0,
195
- )
196
- except ValueError:
197
- return None
198
- else:
199
- return None
200
- except Exception:
201
- return None
202
- elif myDate.find("-") > 0:
203
- dateTuple = myDate.split("-")
204
- try:
205
- return datetime.date(int(dateTuple[0]), int(dateTuple[1]), int(dateTuple[2]))
206
- except Exception:
207
- return None
208
-
209
- return None
210
-
211
-
212
- class timeInterval:
213
- """
214
- Simple class to define a timeInterval object able to check if a given
215
- dateTime is inside
216
- """
217
-
218
- def __init__(self, initialDateTime, intervalTimeDelta):
219
- """
220
- Initialization method, it requires the initial dateTime and the
221
- timedelta that define the limits.
222
- The upper limit is not included thus it is [begin,end)
223
- If not properly initialized an error flag is set, and subsequent calls
224
- to any method will return None
225
- """
226
- if not isinstance(initialDateTime, datetime.datetime) or not isinstance(intervalTimeDelta, datetime.timedelta):
227
- self.__error = True
228
- return None
229
- self.__error = False
230
- if intervalTimeDelta.days < 0:
231
- self.__startDateTime = initialDateTime + intervalTimeDelta
232
- self.__endDateTime = initialDateTime
233
- else:
234
- self.__startDateTime = initialDateTime
235
- self.__endDateTime = initialDateTime + intervalTimeDelta
236
-
237
- def includes(self, myDateTime):
238
- """ """
239
- if self.__error:
240
- return None
241
- if not isinstance(myDateTime, datetime.datetime):
242
- return None
243
- if myDateTime < self.__startDateTime:
244
- return False
245
- if myDateTime >= self.__endDateTime:
246
- return False
247
- return True
248
-
249
-
250
- def queryTime(f):
251
- """Decorator to measure the function call time"""
14
+ from DIRAC import gLogger
252
15
 
253
- def measureQueryTime(*args, **kwargs):
254
- start = time.time()
255
- result = f(*args, **kwargs)
256
- if result["OK"] and "QueryTime" not in result:
257
- result["QueryTime"] = time.time() - start
258
- return result
259
16
 
260
- return measureQueryTime
17
+ timeThis = partial(timeThis, logger_info=gLogger.info)
@@ -19,9 +19,6 @@ def jdl_monkey_business(monkeypatch):
19
19
  monkeypatch.setattr("DIRAC.Core.Base.API.getSites", lambda: S_OK(["LCG.IN2P3.fr"]))
20
20
  monkeypatch.setattr("DIRAC.WorkloadManagementSystem.Utilities.JobModel.getSites", lambda: S_OK(["LCG.IN2P3.fr"]))
21
21
  monkeypatch.setattr("DIRAC.Interfaces.API.Job.getDIRACPlatforms", lambda: S_OK("x86_64-slc6-gcc49-opt"))
22
- monkeypatch.setattr(
23
- "DIRAC.WorkloadManagementSystem.Utilities.JobModel.getDIRACPlatforms", lambda: S_OK("x86_64-slc6-gcc49-opt")
24
- )
25
22
  yield
26
23
 
27
24
 
@@ -29,50 +29,50 @@ def test_base():
29
29
  time.sleep(1)
30
30
  p = Profiler(mainProcess.pid)
31
31
  res = p.pid()
32
- assert res["OK"] is True
32
+ assert res["OK"] is True, res
33
33
  res = p.status()
34
- assert res["OK"] is True
34
+ assert res["OK"] is True, res
35
35
  res = p.runningTime()
36
- assert res["OK"] is True
36
+ assert res["OK"] is True, res
37
37
  assert res["Value"] > 0
38
38
 
39
39
  res = p.memoryUsage()
40
- assert res["OK"] is True
40
+ assert res["OK"] is True, res
41
41
  assert res["Value"] > 0
42
42
  resWC = p.memoryUsage(withChildren=True)
43
- assert resWC["OK"] is True
43
+ assert resWC["OK"] is True, res
44
44
  assert resWC["Value"] > 0
45
45
  assert resWC["Value"] >= res["Value"]
46
46
 
47
47
  res = p.vSizeUsage()
48
- assert res["OK"] is True
48
+ assert res["OK"] is True, res
49
49
  assert res["Value"] > 0
50
50
  resWC = p.vSizeUsage(withChildren=True)
51
- assert resWC["OK"] is True
51
+ assert resWC["OK"] is True, res
52
52
  assert resWC["Value"] > 0
53
53
  assert resWC["Value"] >= res["Value"]
54
54
 
55
55
  res = p.vSizeUsage()
56
- assert res["OK"] is True
56
+ assert res["OK"] is True, res
57
57
  assert res["Value"] > 0
58
58
  resWC = p.vSizeUsage(withChildren=True)
59
- assert resWC["OK"] is True
59
+ assert resWC["OK"] is True, res
60
60
  assert resWC["Value"] > 0
61
61
  assert resWC["Value"] >= res["Value"]
62
62
 
63
63
  res = p.numThreads()
64
- assert res["OK"] is True
64
+ assert res["OK"] is True, res
65
65
  assert res["Value"] > 0
66
66
  resWC = p.numThreads(withChildren=True)
67
- assert resWC["OK"] is True
67
+ assert resWC["OK"] is True, res
68
68
  assert resWC["Value"] > 0
69
69
  assert resWC["Value"] >= res["Value"]
70
70
 
71
71
  res = p.cpuPercentage()
72
- assert res["OK"] is True
72
+ assert res["OK"] is True, res
73
73
  assert res["Value"] >= 0
74
74
  resWC = p.cpuPercentage(withChildren=True)
75
- assert resWC["OK"] is True
75
+ assert resWC["OK"] is True, res
76
76
  assert resWC["Value"] >= 0
77
77
  assert resWC["Value"] >= res["Value"]
78
78
 
@@ -88,13 +88,13 @@ def test_cpuUsage():
88
88
  time.sleep(2)
89
89
  p = Profiler(mainProcess.pid)
90
90
  res = p.pid()
91
- assert res["OK"] is True
91
+ assert res["OK"] is True, res
92
92
  res = p.status()
93
- assert res["OK"] is True
93
+ assert res["OK"] is True, res
94
94
 
95
95
  # user
96
96
  res = p.cpuUsageUser()
97
- assert res["OK"] is True
97
+ assert res["OK"] is True, res
98
98
  assert res["Value"] > 0
99
99
  resC = p.cpuUsageUser(withChildren=True)
100
100
  assert resC["OK"] is True
@@ -102,7 +102,7 @@ def test_cpuUsage():
102
102
  assert resC["Value"] >= res["Value"]
103
103
 
104
104
  res = p.cpuUsageUser()
105
- assert res["OK"] is True
105
+ assert res["OK"] is True, res
106
106
  assert res["Value"] > 0
107
107
  resC = p.cpuUsageUser(withChildren=True)
108
108
  assert resC["OK"] is True
@@ -121,15 +121,15 @@ def test_cpuUsage():
121
121
 
122
122
  # system
123
123
  res = p.cpuUsageSystem()
124
- assert res["OK"] is True
124
+ assert res["OK"] is True, res
125
125
  assert res["Value"] >= 0
126
126
  resWC = p.cpuUsageSystem(withChildren=True)
127
- assert resWC["OK"] is True
127
+ assert resWC["OK"] is True, res
128
128
  assert resWC["Value"] >= 0
129
129
  assert resWC["Value"] >= res["Value"]
130
130
 
131
131
  res = p.cpuUsageSystem()
132
- assert res["OK"] is True
132
+ assert res["OK"] is True, res
133
133
  assert res["Value"] > 0
134
134
  resC = p.cpuUsageSystem(withChildren=True)
135
135
  assert resC["OK"] is True
@@ -25,7 +25,7 @@ def main():
25
25
  localCfg.setConfigurationForAgent(agentName)
26
26
  localCfg.addDefaultEntry("/DIRAC/Security/UseServerCertificate", "yes")
27
27
  localCfg.addDefaultEntry("LogColor", True)
28
- resultDict = localCfg.loadUserData()
28
+ resultDict = localCfg.loadUserData(requireSuccessfulSync=True)
29
29
  if not resultDict["OK"]:
30
30
  gLogger.error("There were errors when loading configuration", resultDict["Message"])
31
31
  sys.exit(1)
@@ -1,47 +1,48 @@
1
- """ Starts a DIRAC command inside an apptainer container.
2
- """
1
+ #!/usr/bin/env python
2
+ """Starts a DIRAC command inside an apptainer container."""
3
3
 
4
4
  import os
5
- import shutil
6
5
  import sys
6
+ from pathlib import Path
7
7
 
8
8
  import DIRAC
9
9
  from DIRAC import S_ERROR, gConfig, gLogger
10
10
  from DIRAC.Core.Base.Script import Script
11
+ from DIRAC.Core.Security.Locations import getCAsLocation, getProxyLocation, getVOMSLocation
12
+ from DIRAC.Core.Utilities.Os import safe_listdir
11
13
  from DIRAC.Core.Utilities.Subprocess import systemCall
12
14
 
13
- CONTAINER_WRAPPER = """#!/bin/bash
14
15
 
15
- echo "Starting inner container wrapper scripts (no install) at `date`."
16
- export DIRAC=%(dirac_env_var)s
17
- export DIRACOS=%(diracos_env_var)s
18
- # In any case we need to find a bashrc, and a cfg
19
- source %(rc_script)s
20
- %(command)s
21
- echo "Finishing inner container wrapper scripts at `date`."
22
- """
16
+ def generate_container_wrapper(dirac_env_var, diracos_env_var, etc_dir, rc_script, command, include_proxy=True):
17
+ lines = [
18
+ "#!/bin/bash",
19
+ f"export DIRAC={dirac_env_var}",
20
+ f"export DIRACOS={diracos_env_var}",
21
+ ]
23
22
 
24
- CONTAINER_DEFROOT = "" # Should add something like "/cvmfs/dirac.egi.eu/container/apptainer/alma9/x86_64"
23
+ if include_proxy:
24
+ lines.append("export X509_USER_PROXY=/etc/proxy")
25
25
 
26
+ lines.extend(
27
+ [
28
+ "export X509_CERT_DIR=/etc/grid-security/certificates",
29
+ "export X509_VOMS_DIR=/etc/grid-security/vomsdir",
30
+ "export X509_VOMSES=/etc/grid-security/vomses",
31
+ f"export DIRACSYSCONFIG={etc_dir}/dirac.cfg",
32
+ f"source {rc_script}",
33
+ command,
34
+ ]
35
+ )
26
36
 
27
- def getEnv():
28
- """Gets the environment for use within the container.
29
- We blank almost everything to prevent contamination from the host system.
30
- """
37
+ return "\n".join(lines)
31
38
 
32
- payloadEnv = {k: v for k, v in os.environ.items()}
33
- payloadEnv["TMP"] = "/tmp"
34
- payloadEnv["TMPDIR"] = "/tmp"
35
- payloadEnv["X509_USER_PROXY"] = os.path.join("tmp", "proxy")
36
- payloadEnv["DIRACSYSCONFIG"] = os.path.join("tmp", "dirac.cfg")
37
39
 
38
- return payloadEnv
40
+ CONTAINER_DEFROOT = "" # Should add something like "/cvmfs/dirac.egi.eu/container/apptainer/alma9/x86_64"
39
41
 
40
42
 
41
43
  @Script()
42
44
  def main():
43
- Script.registerArgument(" command: Command to execute inside the container")
44
- command = Script.getPositionalArgs(group=True)
45
+ command = sys.argv[1]
45
46
 
46
47
  user_image = None
47
48
  Script.registerSwitch("i:", "image=", " apptainer image to use")
@@ -50,41 +51,66 @@ def main():
50
51
  if switch[0].lower() == "i" or switch[0].lower() == "image":
51
52
  user_image = switch[1]
52
53
 
53
- wrapSubs = {
54
- "dirac_env_var": os.environ.get("DIRAC", os.getcwd()),
55
- "diracos_env_var": os.environ.get("DIRACOS", os.getcwd()),
56
- }
57
- wrapSubs["rc_script"] = os.path.join(os.path.realpath(sys.base_prefix), "diracosrc")
58
- wrapSubs["command"] = command
59
- shutil.copyfile("dirac.cfg", os.path.join("tmp", "dirac.cfg"))
60
-
61
- wrapLoc = os.path.join("tmp", "dirac_container.sh")
62
- rawfd = os.open(wrapLoc, os.O_WRONLY | os.O_CREAT, 0o700)
63
- fd = os.fdopen(rawfd, "w")
64
- fd.write(CONTAINER_WRAPPER % wrapSubs)
65
- fd.close()
66
-
67
- innerCmd = os.path.join("tmp", "dirac_container.sh")
54
+ cwd = os.path.realpath(os.getcwd())
55
+ dirac_env_var = os.environ.get("DIRAC", cwd)
56
+ diracos_env_var = os.environ.get("DIRACOS", cwd)
57
+ etc_dir = os.path.join(DIRAC.rootPath, "etc")
58
+ rc_script = os.path.join(os.path.realpath(sys.base_prefix), "diracosrc")
59
+
60
+ include_proxy = True
61
+ proxy_location = getProxyLocation()
62
+ if not proxy_location:
63
+ include_proxy = False
64
+
65
+ with open("dirac_container.sh", "w", encoding="utf-8") as fd:
66
+ script = generate_container_wrapper(
67
+ dirac_env_var, diracos_env_var, etc_dir, rc_script, command, include_proxy=include_proxy
68
+ )
69
+ fd.write(script)
70
+ os.chmod("dirac_container.sh", 0o755)
71
+
72
+ # Now let's construct the apptainer command
68
73
  cmd = ["apptainer", "exec"]
69
74
  cmd.extend(["--contain"]) # use minimal /dev and empty other directories (e.g. /tmp and $HOME)
70
75
  cmd.extend(["--ipc"]) # run container in a new IPC namespace
71
- cmd.extend(["--workdir", "/tmp"]) # working directory to be used for /tmp, /var/tmp and $HOME
72
- cmd.extend(["--home", "/tmp"]) # Avoid using small tmpfs for default $HOME and use scratch /tmp instead
73
- cmd.extend(["--bind", "{0}:{0}:ro".format(os.path.join(os.path.realpath(sys.base_prefix)))])
76
+ cmd.extend(["--pid"]) # run container in a new PID namespace
77
+ cmd.extend(["--bind", cwd]) # bind current directory for dirac_container.sh
78
+ if proxy_location:
79
+ cmd.extend(["--bind", f"{proxy_location}:/etc/proxy"]) # bind proxy file
80
+ cmd.extend(["--bind", f"{getCAsLocation()}:/etc/grid-security/certificates"]) # X509_CERT_DIR
81
+ voms_location = Path(getVOMSLocation())
82
+ cmd.extend(["--bind", f"{voms_location}:/etc/grid-security/vomsdir"]) # X509_VOMS_DIR
83
+ vomses_location = voms_location.parent / "vomses"
84
+ cmd.extend(["--bind", f"{vomses_location}:/etc/grid-security/vomses"]) # X509_VOMSES
85
+ cmd.extend(["--bind", "{0}:{0}:ro".format(etc_dir)]) # etc dir for dirac.cfg
86
+ cmd.extend(["--bind", "{0}:{0}:ro".format(os.path.join(os.path.realpath(sys.base_prefix)))]) # code dir
87
+ # here bind optional paths
88
+ for bind_path in gConfig.getValue("/Resources/Computing/Singularity/BindPaths", []):
89
+ if safe_listdir(bind_path):
90
+ cmd.extend(["--bind", f"{bind_path}:{bind_path}"])
91
+ else:
92
+ gLogger.warn(f"Bind path {bind_path} does not exist, skipping")
93
+ cmd.extend(["--cwd", cwd]) # set working directory
74
94
 
75
95
  rootImage = user_image or gConfig.getValue("/Resources/Computing/Singularity/ContainerRoot") or CONTAINER_DEFROOT
76
96
 
77
97
  if os.path.isdir(rootImage) or os.path.isfile(rootImage):
78
- cmd.extend([rootImage, innerCmd])
98
+ cmd.extend([rootImage, f"{cwd}/dirac_container.sh"])
79
99
  else:
80
100
  # if we are here is because there's no image, or it is not accessible (e.g. not on CVMFS)
81
101
  gLogger.error("Apptainer image to exec not found: ", rootImage)
82
102
  return S_ERROR("Failed to find Apptainer image to exec")
83
103
 
84
- gLogger.debug(f"Execute Apptainer command: {cmd}")
85
- result = systemCall(0, cmd, env=getEnv())
104
+ gLogger.debug(f"Execute Apptainer command: {' '.join(cmd)}")
105
+ result = systemCall(0, cmd)
86
106
  if not result["OK"]:
107
+ gLogger.error(result["Message"])
87
108
  DIRAC.exit(1)
109
+ if result["Value"][0] != 0:
110
+ gLogger.error("Apptainer command failed with exit code", result["Value"][0])
111
+ gLogger.error("Command output:", result["Value"])
112
+ DIRAC.exit(2)
113
+ gLogger.notice(result["Value"][1])
88
114
 
89
115
 
90
116
  if __name__ == "__main__":
@@ -99,9 +99,7 @@ class Params:
99
99
  self.includeAllServers = True
100
100
 
101
101
  def setSetup(self, optionValue):
102
- self.setup = optionValue
103
- DIRAC.gConfig.setOptionValue("/DIRAC/Setup", self.setup)
104
- DIRAC.gConfig.setOptionValue(cfgInstallPath("Setup"), self.setup)
102
+ DIRAC.gLogger.warn("Ignoring Setup option as it is not used for DIRAC v9.0+")
105
103
  return DIRAC.S_OK()
106
104
 
107
105
  def setSiteName(self, optionValue):
@@ -2,6 +2,8 @@
2
2
  """
3
3
  Create a new DB in the MySQL server
4
4
  """
5
+ from DIRAC import exit as DIRACExit
6
+ from DIRAC import gConfig, gLogger
5
7
  from DIRAC.Core.Base.Script import Script
6
8
 
7
9
 
@@ -12,24 +14,40 @@ def main():
12
14
  _, args = Script.parseCommandLine()
13
15
 
14
16
  # Script imports
15
- from DIRAC import gConfig
17
+ from DIRAC.ConfigurationSystem.Client.Helpers.CSGlobals import useServerCertificate
18
+ from DIRAC.Core.Security.ProxyInfo import getProxyInfo
16
19
  from DIRAC.FrameworkSystem.Client.ComponentInstaller import gComponentInstaller
17
20
  from DIRAC.FrameworkSystem.Utilities import MonitoringUtilities
18
21
 
22
+ user = "DIRAC"
23
+
19
24
  gComponentInstaller.exitOnError = True
20
25
  gComponentInstaller.getMySQLPasswords()
21
26
  for db in args:
22
27
  result = gComponentInstaller.installDatabase(db)
23
28
  if not result["OK"]:
24
- print(f"ERROR: failed to correctly install {db}", result["Message"])
25
- continue
29
+ gLogger.error(f"Failed to correctly install {db}:", result["Message"])
30
+ DIRACExit(1)
26
31
  extension, system = result["Value"]
27
- gComponentInstaller.addDatabaseOptionsToCS(gConfig, system, db, overwrite=True)
32
+ result = gComponentInstaller.addDatabaseOptionsToCS(gConfig, system, db, overwrite=True)
33
+ if not result["OK"]:
34
+ gLogger.error("Failed to add database options to CS:", result["Message"])
35
+ DIRACExit(1)
28
36
 
29
37
  if db != "InstalledComponentsDB":
30
- result = MonitoringUtilities.monitorInstallation("DB", system, db)
38
+ # get the user that installed the DB
39
+ if not useServerCertificate():
40
+ result = getProxyInfo()
41
+ if not result["OK"]:
42
+ return result
43
+ proxyInfo = result["Value"]
44
+ if "username" in proxyInfo:
45
+ user = proxyInfo["username"]
46
+
47
+ result = MonitoringUtilities.monitorInstallation("DB", system, db, user=user)
31
48
  if not result["OK"]:
32
- print(f"ERROR: failed to register installation in database: {result['Message']}")
49
+ gLogger.error("Failed to register installation in database:", result["Message"])
50
+ DIRACExit(1)
33
51
 
34
52
 
35
53
  if __name__ == "__main__":