File indexing completed on 2026-04-09 07:58:21
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 from traceback import format_exc
0013
0014 from flask import Blueprint
0015
0016 from idds.common import exceptions
0017 from idds.common.authentication import authenticate_is_super_user
0018 from idds.common.constants import HTTP_STATUS_CODE
0019 from idds.common.constants import RequestStatus
0020 from idds.common.constants import (MessageType, MessageStatus,
0021 MessageSource, MessageDestination,
0022 CommandType)
0023 from idds.common.utils import json_loads
0024 from idds.core.requests import (add_request, get_requests,
0025 get_request, update_request,
0026 get_request_ids_by_name)
0027 from idds.core.messages import add_message
0028 from idds.core.commands import add_command
0029 from idds.rest.v1.controller import IDDSController
0030
0031 from idds.rest.v1.utils import (convert_old_req_2_workflow_req,
0032 get_workflow_item,
0033 get_additional_request_data_storage,
0034 convert_data_to_use_additional_storage,
0035 store_data_to_use_additional_storage)
0036
0037
0038 class Requests(IDDSController):
0039 """ Get request """
0040
0041 def get(self):
0042 """
0043 Get requests.
0044
0045 HTTP Success:
0046 200 OK
0047 HTTP Error:
0048 404 Not Found
0049 500 InternalError
0050 :returns: A list containing requests.
0051 """
0052
0053 try:
0054 request_id = self.get_request().args.get('request_id', None)
0055 workload_id = self.get_request().args.get('workload_id', None)
0056 if request_id is None and workload_id is None:
0057 self.generate_http_response(HTTP_STATUS_CODE.BadRequest,
0058 exc_cls=exceptions.BadRequest.__name__,
0059 exc_msg="request_id and workload_id are both None. One should not be None")
0060
0061 reqs = get_requests(request_id=request_id, workload_id=workload_id)
0062 except exceptions.NoObject as error:
0063 return self.generate_http_response(HTTP_STATUS_CODE.NotFound, exc_cls=error.__class__.__name__, exc_msg=error)
0064 except exceptions.IDDSException as error:
0065 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=error.__class__.__name__, exc_msg=error)
0066 except Exception as error:
0067 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=exceptions.CoreException.__name__, exc_msg=error)
0068
0069 return self.generate_http_response(HTTP_STATUS_CODE.OK, data=reqs)
0070
0071
0072 class Request(IDDSController):
0073 """ Create, Update, get and delete Request. """
0074
0075 def post(self):
0076 """ Create Request.
0077 HTTP Success:
0078 200 OK
0079 HTTP Error:
0080 400 Bad request
0081 500 Internal Error
0082 """
0083 try:
0084 logger = self.get_logger()
0085
0086 parameters = self.get_request().data and json_loads(self.get_request().data)
0087 logger.debug(f"parameters: {parameters}")
0088
0089 workflow = None
0090 if ('request_metadata' in parameters and isinstance(parameters['request_metadata'], dict) and parameters['request_metadata'].get('workflow')):
0091 workflow = parameters['request_metadata']['workflow']
0092
0093 with_add_storage, additional_data_storage = get_additional_request_data_storage(self.get_request().data, workflow, logger)
0094 logger.info(f"additional_data_storage: {additional_data_storage}, with_add_storage: {with_add_storage}")
0095
0096 if workflow and hasattr(workflow, "is_workflow_step") and workflow.is_workflow_step:
0097
0098 internal_id = workflow.get_internal_id()
0099 zip_data = workflow.workflow_data
0100 data = workflow.unzip_data(zip_data)
0101
0102 logger.info(f"Received data for works: {data.keys()}")
0103 store_data_to_use_additional_storage(internal_id, data, additional_data_storage, logger)
0104 return self.generate_http_response(HTTP_STATUS_CODE.OK, data={'request_id': 0})
0105
0106 if 'status' not in parameters:
0107 parameters['status'] = RequestStatus.New
0108 if 'priority' not in parameters:
0109 parameters['priority'] = 0
0110
0111 if 'cloud' not in parameters or not parameters['cloud']:
0112 parameters['cloud'] = get_workflow_item(parameters, 'get_cloud', logger)
0113 if 'site' not in parameters or not parameters['site']:
0114 parameters['site'] = get_workflow_item(parameters, 'get_site', logger)
0115 if 'queue' not in parameters or not parameters['queue']:
0116 parameters['queue'] = get_workflow_item(parameters, 'get_queue', logger)
0117
0118
0119
0120 if 'username' not in parameters or not parameters['username']:
0121 if 'username' in self.get_request().environ and self.get_request().environ['username']:
0122 parameters['username'] = self.get_request().environ['username']
0123
0124 if additional_data_storage:
0125 parameters['additional_data_storage'] = additional_data_storage
0126 except Exception as error:
0127 logger.error(error)
0128 logger.error(format_exc())
0129 return self.generate_http_response(HTTP_STATUS_CODE.BadRequest, exc_cls=exceptions.BadRequest.__name__, exc_msg='Cannot decode json parameter dictionary')
0130
0131 try:
0132 parameters = convert_old_req_2_workflow_req(parameters)
0133 parameters = convert_data_to_use_additional_storage(parameters, additional_data_storage, with_add_storage, logger)
0134 request_id = add_request(**parameters)
0135 except exceptions.DuplicatedObject as error:
0136 logger.error(error)
0137 logger.error(format_exc())
0138 return self.generate_http_response(HTTP_STATUS_CODE.Conflict, exc_cls=error.__class__.__name__, exc_msg=error)
0139 except exceptions.IDDSException as error:
0140 logger.error(error)
0141 logger.error(format_exc())
0142 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=error.__class__.__name__, exc_msg=error)
0143 except Exception as error:
0144 logger.error(error)
0145 logger.error(format_exc())
0146 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=exceptions.CoreException.__name__, exc_msg=error)
0147
0148 return self.generate_http_response(HTTP_STATUS_CODE.OK, data={'request_id': request_id})
0149
0150 def put(self, request_id):
0151 """ Update Request properties with a given id.
0152 HTTP Success:
0153 200 OK
0154 HTTP Error:
0155 400 Bad request
0156 404 Not Found
0157 500 Internal Error
0158 """
0159 try:
0160 logger = self.get_logger()
0161 request = self.get_request()
0162 parameters = request.data and json_loads(request.data)
0163
0164 except ValueError:
0165 return self.generate_http_response(HTTP_STATUS_CODE.BadRequest, exc_cls=exceptions.BadRequest.__name__, exc_msg='Cannot decode json parameter dictionary')
0166
0167 try:
0168 username = self.get_username()
0169 reqs = get_requests(request_id=request_id, with_request=True)
0170 for req in reqs:
0171 if req['username'] and req['username'] != username and not authenticate_is_super_user(username):
0172 raise exceptions.AuthenticationNoPermission("User %s has no permission to update request %s" % (username, req['request_id']))
0173 except exceptions.AuthenticationNoPermission as error:
0174 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=error.__class__.__name__, exc_msg=error)
0175 except Exception as error:
0176 logger.error(error)
0177 logger.error(format_exc())
0178 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=exceptions.CoreException.__name__, exc_msg=error)
0179
0180 try:
0181
0182
0183 msg = {'command': 'update_request', 'parameters': parameters}
0184 add_message(msg_type=MessageType.IDDSCommunication,
0185 status=MessageStatus.New,
0186 destination=MessageDestination.Clerk,
0187 source=MessageSource.Rest,
0188 request_id=request_id,
0189 workload_id=None,
0190 transform_id=None,
0191 num_contents=1,
0192 msg_content=msg)
0193
0194 except exceptions.NoObject as error:
0195 return self.generate_http_response(HTTP_STATUS_CODE.NotFound, exc_cls=error.__class__.__name__, exc_msg=error)
0196 except exceptions.IDDSException as error:
0197 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=error.__class__.__name__, exc_msg=error)
0198 except Exception as error:
0199 logger.error(error)
0200 logger.error(format_exc())
0201 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=exceptions.CoreException.__name__, exc_msg=error)
0202
0203 return self.generate_http_response(HTTP_STATUS_CODE.OK, data={'status': 0, 'message': 'update successfully'})
0204
0205 def get(self, request_id, workload_id, with_detail, with_metadata=False, with_transform=False, with_processing=False):
0206 """ Get details about a specific Request with given id.
0207 HTTP Success:
0208 200 OK
0209 HTTP Error:
0210 404 Not Found
0211 500 InternalError
0212 :returns: dictionary of an request.
0213 """
0214
0215 try:
0216 logger = self.get_logger()
0217 if request_id == 'null':
0218 request_id = None
0219 if workload_id == 'null':
0220 workload_id = None
0221 if with_detail and (type(with_detail) in [bool] or type(with_detail) in [str] and with_detail.lower() in ['true']):
0222 with_detail = True
0223 else:
0224 with_detail = False
0225 if with_metadata and (type(with_metadata) in [bool] or type(with_metadata) in [str] and with_metadata.lower() in ['true']):
0226 with_metadata = True
0227 else:
0228 with_metadata = False
0229 if with_transform and (type(with_transform) in [bool] or type(with_transform) in [str] and with_transform.lower() in ['true']):
0230 with_transform = True
0231 else:
0232 with_transform = False
0233 if with_processing and (type(with_processing) in [bool] or type(with_processing) in [str] and with_processing.lower() in ['true']):
0234 with_processing = True
0235 else:
0236 with_processing = False
0237
0238 if with_detail or with_transform or with_processing:
0239 with_request = False
0240 else:
0241 with_request = True
0242
0243
0244 reqs = get_requests(request_id=request_id, workload_id=workload_id,
0245 with_request=with_request, with_detail=with_detail,
0246 with_metadata=with_metadata, with_transform=with_transform,
0247 with_processing=with_processing)
0248 except exceptions.NoObject as error:
0249 return self.generate_http_response(HTTP_STATUS_CODE.NotFound, exc_cls=error.__class__.__name__, exc_msg=error)
0250 except exceptions.IDDSException as error:
0251 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=error.__class__.__name__, exc_msg=error)
0252 except Exception as error:
0253 logger.error(error)
0254 logger.error(format_exc())
0255 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=exceptions.CoreException.__name__, exc_msg=error)
0256
0257 return self.generate_http_response(HTTP_STATUS_CODE.OK, data=reqs)
0258
0259 def post_test(self):
0260 import pprint
0261 pprint.pprint(self.get_request())
0262 pprint.pprint(self.get_request().endpoint)
0263 pprint.pprint(self.get_request().url_rule)
0264
0265
0266 class RequestName(IDDSController):
0267 """ Get id from name. """
0268
0269 def get(self, name):
0270 """ Get id from name.
0271 HTTP Success:
0272 200 OK
0273 HTTP Error:
0274 404 Not Found
0275 500 InternalError
0276 :returns: {name:id} dict.
0277 """
0278 try:
0279 logger = self.get_logger()
0280 rets = get_request_ids_by_name(name)
0281 except exceptions.NoObject as error:
0282 return self.generate_http_response(HTTP_STATUS_CODE.NotFound, exc_cls=error.__class__.__name__, exc_msg=error)
0283 except exceptions.IDDSException as error:
0284 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=error.__class__.__name__, exc_msg=error)
0285 except Exception as error:
0286 logger.error(error)
0287 logger.error(format_exc())
0288 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=exceptions.CoreException.__name__, exc_msg=error)
0289
0290 return self.generate_http_response(HTTP_STATUS_CODE.OK, data=rets)
0291
0292
0293 class RequestBuild(IDDSController):
0294 """ Create, Update, get and delete Request. """
0295
0296 def post(self, request_id):
0297 """ update build request result.
0298 HTTP Success:
0299 200 OK
0300 HTTP Error:
0301 400 Bad request
0302 500 Internal Error
0303 """
0304 try:
0305 logger = self.get_logger()
0306 parameters = self.get_request().data and json_loads(self.get_request().data)
0307 if 'signature' not in parameters or 'workflow' not in parameters:
0308 raise exceptions.IDDSException("signature and workflow are required")
0309
0310
0311 signature = parameters['signature']
0312 workflow = parameters['workflow']
0313
0314 req = get_request(request_id=request_id)
0315 if not req:
0316 raise exceptions.IDDSException("Request %s is not found" % request_id)
0317
0318 build_workflow = req['request_metadata']['build_workflow']
0319 works = build_workflow.get_all_works()
0320 build_work = works[0]
0321 if build_work.get_signature() != signature:
0322 raise exceptions.IDDSException("Request (request_id: %s) has a different signature(%s != %s)" % (request_id,
0323 signature,
0324 build_work.get_signature()))
0325 if 'workflow' in req['request_metadata'] and req['request_metadata']['workflow'] is not None:
0326 raise exceptions.IDDSException(f"Request(request_id: {request_id}, status: {req['status']}) already has defined workflow")
0327
0328
0329
0330 with_add_storage, additional_data_storage = get_additional_request_data_storage(self.get_request().data, workflow, logger)
0331 logger.info(f"additional_data_storage: {additional_data_storage}, with_add_storage: {with_add_storage}")
0332
0333 data = {"request_metadata": {"workflow": workflow}}
0334 data = convert_data_to_use_additional_storage(data, additional_data_storage, with_add_storage, logger)
0335
0336 parameters = {'status': RequestStatus.Built,
0337 'request_metadata': data['request_metadata']}
0338 update_request(request_id=req['request_id'], parameters=parameters, update_request_metadata=True)
0339 except exceptions.IDDSException as error:
0340 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=error.__class__.__name__, exc_msg=error)
0341 except Exception as error:
0342 logger.error(error)
0343 logger.error(format_exc())
0344 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=exceptions.CoreException.__name__, exc_msg=error)
0345
0346 return self.generate_http_response(HTTP_STATUS_CODE.OK, data={'request_id': request_id})
0347
0348
0349 class RequestAbort(IDDSController):
0350 """ Abort Request. """
0351
0352 def put(self, request_id, workload_id=None, task_id=None):
0353 """ Abort the request.
0354 HTTP Success:
0355 200 OK
0356 HTTP Error:
0357 400 Bad request
0358 404 Not Found
0359 500 Internal Error
0360 """
0361 if request_id == 'null':
0362 request_id = None
0363 if workload_id == 'null':
0364 workload_id = None
0365 if task_id == 'null':
0366 task_id = None
0367
0368 try:
0369 logger = self.get_logger()
0370 username = self.get_username()
0371 if task_id:
0372 reqs = get_requests(request_id=request_id, workload_id=workload_id, with_processing=True)
0373 else:
0374 reqs = get_requests(request_id=request_id, workload_id=workload_id, with_request=True)
0375
0376 if not reqs:
0377 return self.generate_http_response(HTTP_STATUS_CODE.OK, data=[(-1, {'status': -1, 'message': 'No match requests'})])
0378 matched_transform_id = None
0379 if task_id:
0380 for req in reqs:
0381 if str(req['processing_workload_id']) == str(task_id):
0382 matched_transform_id = req['transform_id']
0383 if matched_transform_id:
0384 return self.generate_http_response(HTTP_STATUS_CODE.OK, data=[(-1, {'status': -1, 'message': 'No match tasks'})])
0385
0386 for req in reqs:
0387 if req['username'] and req['username'] != username and not authenticate_is_super_user(username):
0388 msg = "User %s has no permission to update request %s" % (username, req['request_id'])
0389
0390 return self.generate_http_response(HTTP_STATUS_CODE.OK, data=[(-1, {'status': -1, 'message': msg})])
0391 except exceptions.AuthenticationNoPermission as error:
0392 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=error.__class__.__name__, exc_msg=error)
0393 except Exception as error:
0394 logger.error(error)
0395 logger.error(format_exc())
0396 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=exceptions.CoreException.__name__, exc_msg=error)
0397
0398 try:
0399 cmd_content = None
0400 if task_id and matched_transform_id:
0401 cmd_content = {'task_id': task_id,
0402 'transform_id': matched_transform_id}
0403
0404 add_command(request_id=request_id, cmd_type=CommandType.AbortRequest,
0405 workload_id=workload_id, cmd_content=cmd_content,
0406 username=username)
0407
0408 except exceptions.NoObject as error:
0409 return self.generate_http_response(HTTP_STATUS_CODE.NotFound, exc_cls=error.__class__.__name__, exc_msg=error)
0410 except exceptions.IDDSException as error:
0411 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=error.__class__.__name__, exc_msg=error)
0412 except Exception as error:
0413 logger.error(error)
0414 logger.error(format_exc())
0415 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=exceptions.CoreException.__name__, exc_msg=error)
0416
0417 return self.generate_http_response(HTTP_STATUS_CODE.OK, data=[(0, {'status': 0, 'message': 'Command registered successfully'})])
0418
0419
0420 class RequestClose(IDDSController):
0421 """ Clsoe Request. """
0422
0423 def put(self, request_id, workload_id=None):
0424 """ Close the request.
0425 HTTP Success:
0426 200 OK
0427 HTTP Error:
0428 400 Bad request
0429 404 Not Found
0430 500 Internal Error
0431 """
0432 if request_id == 'null':
0433 request_id = None
0434 if workload_id == 'null':
0435 workload_id = None
0436
0437 try:
0438 logger = self.get_logger()
0439 username = self.get_username()
0440 reqs = get_requests(request_id=request_id, workload_id=workload_id, with_request=True)
0441
0442 if not reqs:
0443 return self.generate_http_response(HTTP_STATUS_CODE.OK, data=[(-1, {'status': -1, 'message': 'No match requests'})])
0444
0445 for req in reqs:
0446 if req['username'] and req['username'] != username and not authenticate_is_super_user(username):
0447 msg = "User %s has no permission to update request %s(user: %s)" % (username, req['request_id'], req['username'])
0448
0449 return self.generate_http_response(HTTP_STATUS_CODE.OK, data=[(-1, {'status': -1, 'message': msg})])
0450 except exceptions.AuthenticationNoPermission as error:
0451 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=error.__class__.__name__, exc_msg=error)
0452 except Exception as error:
0453 logger.error(error)
0454 logger.error(format_exc())
0455 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=exceptions.CoreException.__name__, exc_msg=error)
0456
0457 try:
0458 add_command(request_id=request_id, cmd_type=CommandType.CloseRequest,
0459 workload_id=workload_id, cmd_content=None, username=username)
0460
0461 except exceptions.NoObject as error:
0462 return self.generate_http_response(HTTP_STATUS_CODE.NotFound, exc_cls=error.__class__.__name__, exc_msg=error)
0463 except exceptions.IDDSException as error:
0464 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=error.__class__.__name__, exc_msg=error)
0465 except Exception as error:
0466 logger.error(error)
0467 logger.error(format_exc())
0468 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=exceptions.CoreException.__name__, exc_msg=error)
0469
0470 return self.generate_http_response(HTTP_STATUS_CODE.OK, data=[(0, {'status': 0, 'message': 'Command registered successfully'})])
0471
0472
0473 class RequestRetry(IDDSController):
0474 """ Retry Request. """
0475
0476 def put(self, request_id, workload_id=None):
0477 """ Retry the request.
0478 HTTP Success:
0479 200 OK
0480 HTTP Error:
0481 400 Bad request
0482 404 Not Found
0483 500 Internal Error
0484 """
0485
0486 if request_id == 'null':
0487 request_id = None
0488 if workload_id == 'null':
0489 workload_id = None
0490
0491 try:
0492 logger = self.get_logger()
0493 username = self.get_username()
0494 reqs = get_requests(request_id=request_id, workload_id=workload_id, with_request=True)
0495 if not reqs:
0496 return self.generate_http_response(HTTP_STATUS_CODE.OK, data=[(-1, {'status': -1, 'message': 'No match requests'})])
0497
0498 for req in reqs:
0499 if req['username'] and req['username'] != username and not authenticate_is_super_user(username):
0500 msg = "User %s has no permission to update request %s" % (username, req['request_id'])
0501
0502 return self.generate_http_response(HTTP_STATUS_CODE.OK, data=[(-1, {'status': -1, 'message': msg})])
0503 except exceptions.AuthenticationNoPermission as error:
0504 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=error.__class__.__name__, exc_msg=error)
0505 except Exception as error:
0506 logger.error(error)
0507 logger.error(format_exc())
0508 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=exceptions.CoreException.__name__, exc_msg=error)
0509
0510 try:
0511 add_command(request_id=request_id, cmd_type=CommandType.ResumeRequest,
0512 workload_id=workload_id, cmd_content=None,
0513 username=username)
0514
0515 except exceptions.NoObject as error:
0516 return self.generate_http_response(HTTP_STATUS_CODE.NotFound, exc_cls=error.__class__.__name__, exc_msg=error)
0517 except exceptions.IDDSException as error:
0518 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=error.__class__.__name__, exc_msg=error)
0519 except Exception as error:
0520 logger.error(error)
0521 logger.error(format_exc())
0522 return self.generate_http_response(HTTP_STATUS_CODE.InternalError, exc_cls=exceptions.CoreException.__name__, exc_msg=error)
0523
0524 return self.generate_http_response(HTTP_STATUS_CODE.OK, data=[(0, {'status': 0, 'message': 'Command registered successfully'})])
0525
0526
0527 """----------------------
0528 Web service url maps
0529 ----------------------"""
0530
0531
0532 def get_blueprint():
0533 bp = Blueprint('request', __name__)
0534
0535 request_view = Request.as_view('request')
0536 bp.add_url_rule('/request', view_func=request_view, methods=['post', ])
0537 bp.add_url_rule('/request/<request_id>', view_func=request_view, methods=['put', ])
0538 bp.add_url_rule('/request/<request_id>/<workload_id>/<with_detail>', view_func=request_view, methods=['get', ])
0539 bp.add_url_rule('/request/<request_id>/<workload_id>/<with_detail>/<with_metadata>', view_func=request_view, methods=['get', ])
0540 bp.add_url_rule('/request/<request_id>/<workload_id>/<with_detail>/<with_metadata>/<with_transform>', view_func=request_view, methods=['get', ])
0541 bp.add_url_rule('/request/<request_id>/<workload_id>/<with_detail>/<with_metadata>/<with_transform>/<with_processing>', view_func=request_view, methods=['get', ])
0542
0543 request_name2id = RequestName.as_view('request_name')
0544 bp.add_url_rule('/request/name/<name>', view_func=request_name2id, methods=['get', ])
0545
0546 request_build = RequestBuild.as_view('request_build')
0547 bp.add_url_rule('/request/build/<request_id>', view_func=request_build, methods=['post', ])
0548
0549 request_abort = RequestAbort.as_view('request_abort')
0550 bp.add_url_rule('/request/abort/<request_id>/<workload_id>', view_func=request_abort, methods=['put', ])
0551 bp.add_url_rule('/request/abort/<request_id>/<workload_id>/task_id', view_func=request_abort, methods=['put', ])
0552
0553 request_close = RequestClose.as_view('request_close')
0554 bp.add_url_rule('/request/close/<request_id>/<workload_id>', view_func=request_close, methods=['put', ])
0555
0556 request_retry = RequestRetry.as_view('request_retry')
0557 bp.add_url_rule('/request/retry/<request_id>/<workload_id>', view_func=request_retry, methods=['put', ])
0558
0559 return bp