|
67 | 67 | import os |
68 | 68 | import shutil |
69 | 69 | import stat |
| 70 | +import tempfile |
70 | 71 | import uuid |
71 | 72 | from shlex import quote as shlex_quote |
72 | | -from urllib.parse import quote, unquote, urlparse |
| 73 | +from urllib.parse import urlparse |
73 | 74 |
|
74 | 75 | import pexpect |
75 | 76 |
|
@@ -484,47 +485,69 @@ def __executeHostCommand(self, command, options, ssh=None, host=None): |
484 | 485 | options["User"] = self.user |
485 | 486 | options["Queue"] = self.queue |
486 | 487 |
|
487 | | - options = json.dumps(options) |
488 | | - options = quote(options) |
| 488 | + localOptionsFile = None |
| 489 | + remoteOptionsFile = None |
| 490 | + localResultFile = None |
| 491 | + remoteResultFile = None |
| 492 | + try: |
| 493 | + # Write options to a local temporary file |
| 494 | + with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f: |
| 495 | + json.dump(options, f) |
| 496 | + localOptionsFile = f.name |
| 497 | + |
| 498 | + # Upload the options file to the remote host |
| 499 | + remoteOptionsFile = f"{self.sharedArea}/batch_options_{uuid.uuid4().hex}.json" |
| 500 | + result = ssh.scpCall(30, localOptionsFile, remoteOptionsFile) |
| 501 | + if not result["OK"]: |
| 502 | + return result |
489 | 503 |
|
490 | | - cmd = ( |
491 | | - "bash --login -c 'python3 %s/execute_batch %s || python %s/execute_batch %s || python2 %s/execute_batch %s'" |
492 | | - % (self.sharedArea, options, self.sharedArea, options, self.sharedArea, options) |
493 | | - ) |
| 504 | + # Execute the batch command with the options file path |
| 505 | + cmd = ( |
| 506 | + f"bash --login -c 'python3 {self.sharedArea}/execute_batch {remoteOptionsFile} || " |
| 507 | + f"python {self.sharedArea}/execute_batch {remoteOptionsFile} || " |
| 508 | + f"python2 {self.sharedArea}/execute_batch {remoteOptionsFile}'" |
| 509 | + ) |
494 | 510 |
|
495 | | - self.log.verbose(f"CE submission command: {cmd}") |
| 511 | + self.log.verbose(f"CE submission command: {cmd}") |
496 | 512 |
|
497 | | - result = ssh.sshCall(120, cmd) |
498 | | - if not result["OK"]: |
499 | | - self.log.error(f"{self.ceType} CE job submission failed", result["Message"]) |
500 | | - return result |
| 513 | + result = ssh.sshCall(120, cmd) |
| 514 | + if not result["OK"]: |
| 515 | + self.log.error(f"{self.ceType} CE job submission failed", result["Message"]) |
| 516 | + return result |
501 | 517 |
|
502 | | - sshStatus = result["Value"][0] |
503 | | - sshStdout = result["Value"][1] |
504 | | - sshStderr = result["Value"][2] |
505 | | - |
506 | | - # Examine results of the job submission |
507 | | - if sshStatus == 0: |
508 | | - output = sshStdout.strip().replace("\r", "").strip() |
509 | | - if not output: |
510 | | - return S_ERROR("No output from remote command") |
511 | | - |
512 | | - try: |
513 | | - index = output.index("============= Start output ===============") |
514 | | - output = output[index + 42 :] |
515 | | - except ValueError: |
516 | | - return S_ERROR(f"Invalid output from remote command: {output}") |
517 | | - |
518 | | - try: |
519 | | - output = unquote(output) |
520 | | - result = json.loads(output) |
521 | | - if isinstance(result, str) and result.startswith("Exception:"): |
522 | | - return S_ERROR(result) |
523 | | - return S_OK(result) |
524 | | - except Exception: |
525 | | - return S_ERROR("Invalid return structure from job submission") |
526 | | - else: |
527 | | - return S_ERROR("\n".join([sshStdout, sshStderr])) |
| 518 | + sshStatus = result["Value"][0] |
| 519 | + if sshStatus != 0: |
| 520 | + sshStdout = result["Value"][1] |
| 521 | + sshStderr = result["Value"][2] |
| 522 | + return S_ERROR(f"CE job submission command failed with status {sshStatus}: {sshStdout} {sshStderr}") |
| 523 | + |
| 524 | + # The result should be written to a JSON file by execute_batch |
| 525 | + # Compute the expected result file path |
| 526 | + remoteResultFile = remoteOptionsFile.replace(".json", "_result.json") |
| 527 | + |
| 528 | + # Try to download the result file |
| 529 | + with tempfile.NamedTemporaryFile(mode="r", suffix=".json", delete=False) as f: |
| 530 | + localResultFile = f.name |
| 531 | + |
| 532 | + result = ssh.scpCall(30, localResultFile, remoteResultFile, upload=False) |
| 533 | + if not result["OK"]: |
| 534 | + return result |
| 535 | + |
| 536 | + # Read the result from the downloaded file |
| 537 | + with open(localResultFile) as f: |
| 538 | + result = json.load(f) |
| 539 | + return S_OK(result) |
| 540 | + finally: |
| 541 | + # Clean up local temporary file |
| 542 | + if localOptionsFile and os.path.exists(localOptionsFile): |
| 543 | + os.remove(localOptionsFile) |
| 544 | + if localResultFile and os.path.exists(localResultFile): |
| 545 | + os.remove(localResultFile) |
| 546 | + # Clean up remote temporary files |
| 547 | + if remoteOptionsFile: |
| 548 | + ssh.sshCall(30, f"rm -f {remoteOptionsFile}") |
| 549 | + if remoteResultFile: |
| 550 | + ssh.sshCall(30, f"rm -f {remoteResultFile}") |
528 | 551 |
|
529 | 552 | def submitJob(self, executableFile, proxy, numberOfJobs=1): |
530 | 553 | # self.log.verbose( "Executable file path: %s" % executableFile ) |
|
0 commit comments