lsst-ctrl-bps-panda 29.2025.2100__tar.gz → 29.2025.2300__tar.gz
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.
- {lsst_ctrl_bps_panda-29.2025.2100/python/lsst_ctrl_bps_panda.egg-info → lsst_ctrl_bps_panda-29.2025.2300}/PKG-INFO +1 -1
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst/ctrl/bps/panda/panda_service.py +41 -0
- lsst_ctrl_bps_panda-29.2025.2300/python/lsst/ctrl/bps/panda/version.py +2 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300/python/lsst_ctrl_bps_panda.egg-info}/PKG-INFO +1 -1
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst_ctrl_bps_panda.egg-info/SOURCES.txt +1 -0
- lsst_ctrl_bps_panda-29.2025.2300/tests/test_panda_service.py +194 -0
- lsst_ctrl_bps_panda-29.2025.2100/python/lsst/ctrl/bps/panda/version.py +0 -2
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/COPYRIGHT +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/LICENSE +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/README.rst +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/bsd_license.txt +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/gpl-v3.0.txt +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/pyproject.toml +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst/ctrl/bps/panda/__init__.py +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst/ctrl/bps/panda/cli/__init__.py +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst/ctrl/bps/panda/cli/cmd/__init__.py +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst/ctrl/bps/panda/cli/cmd/panda_auth_commands.py +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst/ctrl/bps/panda/cli/panda_auth.py +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst/ctrl/bps/panda/cmd_line_embedder.py +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst/ctrl/bps/panda/conf_example/example_panda_SLAC.yaml +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst/ctrl/bps/panda/conf_example/pipelines_check_idf.yaml +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst/ctrl/bps/panda/conf_example/test_idf.yaml +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst/ctrl/bps/panda/conf_example/test_sdf.yaml +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst/ctrl/bps/panda/conf_example/test_usdf.yaml +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst/ctrl/bps/panda/constants.py +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst/ctrl/bps/panda/edgenode/__init__.py +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst/ctrl/bps/panda/edgenode/build_cmd_line_decoder.py +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst/ctrl/bps/panda/edgenode/cmd_line_decoder.py +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst/ctrl/bps/panda/panda_auth_drivers.py +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst/ctrl/bps/panda/panda_auth_utils.py +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst/ctrl/bps/panda/utils.py +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst_ctrl_bps_panda.egg-info/dependency_links.txt +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst_ctrl_bps_panda.egg-info/requires.txt +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst_ctrl_bps_panda.egg-info/top_level.txt +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/python/lsst_ctrl_bps_panda.egg-info/zip-safe +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/setup.cfg +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/tests/test_cmd_line_embedder.py +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/tests/test_panda_auth_utils.py +0 -0
- {lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/tests/test_utils.py +0 -0
|
@@ -379,6 +379,47 @@ class PanDAService(BaseWmsService):
|
|
|
379
379
|
if status != 0:
|
|
380
380
|
raise RuntimeError(message)
|
|
381
381
|
|
|
382
|
+
def get_status(
|
|
383
|
+
self,
|
|
384
|
+
wms_workflow_id=None,
|
|
385
|
+
hist=0,
|
|
386
|
+
is_global=False,
|
|
387
|
+
):
|
|
388
|
+
# Docstring inherited from BaseWmsService.get_status.
|
|
389
|
+
|
|
390
|
+
idds_client = get_idds_client(self.config)
|
|
391
|
+
ret = idds_client.get_requests(request_id=wms_workflow_id, with_detail=False)
|
|
392
|
+
_LOG.debug("PanDA get workflow status returned = %s", str(ret))
|
|
393
|
+
|
|
394
|
+
request_status = ret[0]
|
|
395
|
+
if request_status != 0:
|
|
396
|
+
state = WmsStates.UNKNOWN
|
|
397
|
+
message = f"Error getting workflow status for id {wms_workflow_id}: ret = {ret}"
|
|
398
|
+
else:
|
|
399
|
+
tasks = ret[1][1]
|
|
400
|
+
if not tasks:
|
|
401
|
+
state = WmsStates.UNKNOWN
|
|
402
|
+
message = f"No records found for workflow id '{wms_workflow_id}'. Hint: double check the id"
|
|
403
|
+
elif not isinstance(tasks[0], dict):
|
|
404
|
+
state = WmsStates.UNKNOWN
|
|
405
|
+
message = f"Error getting workflow status for id {wms_workflow_id}: ret = {ret}"
|
|
406
|
+
else:
|
|
407
|
+
message = ""
|
|
408
|
+
head = tasks[0]
|
|
409
|
+
workflow_status = head["status"]["attributes"]["_name_"]
|
|
410
|
+
if workflow_status in ["Finished"]:
|
|
411
|
+
state = WmsStates.SUCCEEDED
|
|
412
|
+
elif workflow_status in ["Failed", "Expired", "SubFinished"]:
|
|
413
|
+
state = WmsStates.FAILED
|
|
414
|
+
elif workflow_status in ["Cancelled"]:
|
|
415
|
+
state = WmsStates.DELETED
|
|
416
|
+
elif workflow_status in ["Suspended"]:
|
|
417
|
+
state = WmsStates.HELD
|
|
418
|
+
else:
|
|
419
|
+
state = WmsStates.RUNNING
|
|
420
|
+
|
|
421
|
+
return state, message
|
|
422
|
+
|
|
382
423
|
|
|
383
424
|
class PandaBpsWmsWorkflow(BaseWmsWorkflow):
|
|
384
425
|
"""A single Panda based workflow.
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# This file is part of ctrl_bps_panda.
|
|
2
|
+
#
|
|
3
|
+
# Developed for the LSST Data Management System.
|
|
4
|
+
# This product includes software developed by the LSST Project
|
|
5
|
+
# (https://www.lsst.org).
|
|
6
|
+
# See the COPYRIGHT file at the top-level directory of this distribution
|
|
7
|
+
# for details of code ownership.
|
|
8
|
+
#
|
|
9
|
+
# This software is dual licensed under the GNU General Public License and also
|
|
10
|
+
# under a 3-clause BSD license. Recipients may choose which of these licenses
|
|
11
|
+
# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
|
|
12
|
+
# respectively. If you choose the GPL option then the following text applies
|
|
13
|
+
# (but note that there is still no warranty even if you opt for BSD instead):
|
|
14
|
+
#
|
|
15
|
+
# This program is free software: you can redistribute it and/or modify
|
|
16
|
+
# it under the terms of the GNU General Public License as published by
|
|
17
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
18
|
+
# (at your option) any later version.
|
|
19
|
+
#
|
|
20
|
+
# This program is distributed in the hope that it will be useful,
|
|
21
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
22
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
23
|
+
# GNU General Public License for more details.
|
|
24
|
+
#
|
|
25
|
+
# You should have received a copy of the GNU General Public License
|
|
26
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
27
|
+
|
|
28
|
+
"""Unit tests for ctrl_bps_panda panda_service module."""
|
|
29
|
+
|
|
30
|
+
import logging
|
|
31
|
+
import unittest
|
|
32
|
+
|
|
33
|
+
from idds.common.constants import WorkStatus
|
|
34
|
+
|
|
35
|
+
from lsst.ctrl.bps import BpsConfig, WmsStates
|
|
36
|
+
from lsst.ctrl.bps.panda import panda_service
|
|
37
|
+
|
|
38
|
+
_LOG = logging.getLogger(__name__)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class MockClient:
|
|
42
|
+
"""Mock idds client."""
|
|
43
|
+
|
|
44
|
+
def __init__(self):
|
|
45
|
+
_LOG.debug("Called mock client init")
|
|
46
|
+
|
|
47
|
+
def get_requests(self, request_id, with_detail):
|
|
48
|
+
_LOG.debug("Called mock client get_requests with %s", request_id)
|
|
49
|
+
status = None
|
|
50
|
+
match request_id:
|
|
51
|
+
case "1000": # GetRequestsFailure
|
|
52
|
+
requests = (1, "PanDA error message")
|
|
53
|
+
case "1001":
|
|
54
|
+
status = WorkStatus.Finished
|
|
55
|
+
case "1002":
|
|
56
|
+
status = WorkStatus.SubFinished
|
|
57
|
+
case "1003":
|
|
58
|
+
status = WorkStatus.Failed
|
|
59
|
+
case "1004":
|
|
60
|
+
status = WorkStatus.Cancelled
|
|
61
|
+
case "1005":
|
|
62
|
+
status = WorkStatus.Suspended
|
|
63
|
+
case "1006":
|
|
64
|
+
status = WorkStatus.Running
|
|
65
|
+
case "1007":
|
|
66
|
+
status = WorkStatus.Transforming
|
|
67
|
+
case "1008":
|
|
68
|
+
requests = (0, [True, [False, "An unknown IDDS exception occurred."]])
|
|
69
|
+
case _: # Unknown ID
|
|
70
|
+
requests = (0, [True, []])
|
|
71
|
+
|
|
72
|
+
if status:
|
|
73
|
+
workflow_name = "FAKE_WORKFLOW_NAME_20250515T213417Z"
|
|
74
|
+
requests = (
|
|
75
|
+
0,
|
|
76
|
+
[
|
|
77
|
+
True,
|
|
78
|
+
[
|
|
79
|
+
{
|
|
80
|
+
"name": workflow_name,
|
|
81
|
+
"request_id": request_id,
|
|
82
|
+
"status": {
|
|
83
|
+
"attributes": {
|
|
84
|
+
"_value_": status.value,
|
|
85
|
+
"_name_": status.name,
|
|
86
|
+
"_sort_order_": status.value,
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
],
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
return requests
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class TestPanDAService(unittest.TestCase):
|
|
98
|
+
"""Test PanDAService class methods."""
|
|
99
|
+
|
|
100
|
+
def setUp(self):
|
|
101
|
+
config = BpsConfig({}, wms_service_class_fqn="lsst.ctrl.bps.panda.PanDAService")
|
|
102
|
+
self.service = panda_service.PanDAService(config)
|
|
103
|
+
|
|
104
|
+
@unittest.mock.patch("lsst.ctrl.bps.panda.panda_service.get_idds_client")
|
|
105
|
+
def testGetStatusGetRequestsFailure(self, mock_get):
|
|
106
|
+
mock_get.return_value = MockClient()
|
|
107
|
+
status, message = self.service.get_status("1000")
|
|
108
|
+
|
|
109
|
+
self.assertEqual(status, WmsStates.UNKNOWN)
|
|
110
|
+
self.assertEqual(
|
|
111
|
+
message, "Error getting workflow status for id 1000: ret = (1, 'PanDA error message')"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
@unittest.mock.patch("lsst.ctrl.bps.panda.panda_service.get_idds_client")
|
|
115
|
+
def testGetStatusUnknownID(self, mock_get):
|
|
116
|
+
mock_get.return_value = MockClient()
|
|
117
|
+
status, message = self.service.get_status("9999")
|
|
118
|
+
|
|
119
|
+
self.assertEqual(status, WmsStates.UNKNOWN)
|
|
120
|
+
self.assertEqual(message, "No records found for workflow id '9999'. Hint: double check the id")
|
|
121
|
+
|
|
122
|
+
@unittest.mock.patch("lsst.ctrl.bps.panda.panda_service.get_idds_client")
|
|
123
|
+
def testGetStatusFinished(self, mock_get):
|
|
124
|
+
mock_get.return_value = MockClient()
|
|
125
|
+
status, message = self.service.get_status("1001")
|
|
126
|
+
|
|
127
|
+
self.assertEqual(status, WmsStates.SUCCEEDED)
|
|
128
|
+
self.assertEqual(message, "")
|
|
129
|
+
|
|
130
|
+
@unittest.mock.patch("lsst.ctrl.bps.panda.panda_service.get_idds_client")
|
|
131
|
+
def testGetStatusSubFinished(self, mock_get):
|
|
132
|
+
mock_get.return_value = MockClient()
|
|
133
|
+
status, message = self.service.get_status("1002")
|
|
134
|
+
|
|
135
|
+
self.assertEqual(status, WmsStates.FAILED)
|
|
136
|
+
self.assertEqual(message, "")
|
|
137
|
+
|
|
138
|
+
@unittest.mock.patch("lsst.ctrl.bps.panda.panda_service.get_idds_client")
|
|
139
|
+
def testGetStatusFailed(self, mock_get):
|
|
140
|
+
mock_get.return_value = MockClient()
|
|
141
|
+
status, message = self.service.get_status("1003")
|
|
142
|
+
|
|
143
|
+
self.assertEqual(status, WmsStates.FAILED)
|
|
144
|
+
self.assertEqual(message, "")
|
|
145
|
+
|
|
146
|
+
@unittest.mock.patch("lsst.ctrl.bps.panda.panda_service.get_idds_client")
|
|
147
|
+
def testGetStatusCancelled(self, mock_get):
|
|
148
|
+
mock_get.return_value = MockClient()
|
|
149
|
+
status, message = self.service.get_status("1004")
|
|
150
|
+
|
|
151
|
+
self.assertEqual(status, WmsStates.DELETED)
|
|
152
|
+
self.assertEqual(message, "")
|
|
153
|
+
|
|
154
|
+
@unittest.mock.patch("lsst.ctrl.bps.panda.panda_service.get_idds_client")
|
|
155
|
+
def testGetStatusSuspended(self, mock_get):
|
|
156
|
+
mock_get.return_value = MockClient()
|
|
157
|
+
status, message = self.service.get_status("1005")
|
|
158
|
+
|
|
159
|
+
self.assertEqual(status, WmsStates.HELD)
|
|
160
|
+
self.assertEqual(message, "")
|
|
161
|
+
|
|
162
|
+
@unittest.mock.patch("lsst.ctrl.bps.panda.panda_service.get_idds_client")
|
|
163
|
+
def testGetStatusRunning(self, mock_get):
|
|
164
|
+
mock_get.return_value = MockClient()
|
|
165
|
+
status, message = self.service.get_status("1006")
|
|
166
|
+
|
|
167
|
+
self.assertEqual(status, WmsStates.RUNNING)
|
|
168
|
+
self.assertEqual(message, "")
|
|
169
|
+
|
|
170
|
+
@unittest.mock.patch("lsst.ctrl.bps.panda.panda_service.get_idds_client")
|
|
171
|
+
def testGetStatusTransforming(self, mock_get):
|
|
172
|
+
mock_get.return_value = MockClient()
|
|
173
|
+
status, message = self.service.get_status("1007")
|
|
174
|
+
|
|
175
|
+
self.assertEqual(status, WmsStates.RUNNING)
|
|
176
|
+
self.assertEqual(message, "")
|
|
177
|
+
|
|
178
|
+
@unittest.mock.patch("lsst.ctrl.bps.panda.panda_service.get_idds_client")
|
|
179
|
+
def testGetStatusUnknownIDDSException(self, mock_get):
|
|
180
|
+
# Test example unknown IDDS exception similar to what occurs
|
|
181
|
+
# if give path to ctrl_bps_panda's get_status.
|
|
182
|
+
mock_get.return_value = MockClient()
|
|
183
|
+
status, message = self.service.get_status("1008")
|
|
184
|
+
|
|
185
|
+
self.assertEqual(status, WmsStates.UNKNOWN)
|
|
186
|
+
self.assertEqual(
|
|
187
|
+
message,
|
|
188
|
+
"Error getting workflow status for id 1008: ret = "
|
|
189
|
+
"(0, [True, [False, 'An unknown IDDS exception occurred.']])",
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
if __name__ == "__main__":
|
|
194
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lsst_ctrl_bps_panda-29.2025.2100 → lsst_ctrl_bps_panda-29.2025.2300}/tests/test_panda_auth_utils.py
RENAMED
|
File without changes
|
|
File without changes
|