add poll_job to track job submissions
This commit is contained in:
parent
bc4a50d475
commit
b23e0f69bb
192
poll_job.py
Executable file
192
poll_job.py
Executable file
@ -0,0 +1,192 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import subprocess
|
||||
import requests
|
||||
from requests.auth import HTTPBasicAuth
|
||||
|
||||
CONSOLE_URL = os.environ.get("MVS_CONSOLE_URL")
|
||||
CONSOLE_USER = os.environ.get("MVS_CONSOLE_USER")
|
||||
CONSOLE_PASS = os.environ.get("MVS_CONSOLE_PASSWORD")
|
||||
LINODE_HOST = os.environ.get("LINODE_SSH_HOST")
|
||||
LINODE_PRINTOUT_DIR = os.environ.get("LINODE_PRINTOUT_DIR")
|
||||
|
||||
def get_syslog():
|
||||
"""Fetch the Hercules syslog via HTTP"""
|
||||
try:
|
||||
response = requests.get(
|
||||
CONSOLE_URL,
|
||||
auth=HTTPBasicAuth(CONSOLE_USER, CONSOLE_PASS),
|
||||
timeout=10
|
||||
)
|
||||
response.raise_for_status()
|
||||
return response.text
|
||||
except requests.RequestException as e:
|
||||
print(f"Failed to fetch syslog: {e}")
|
||||
return None
|
||||
|
||||
def find_job_number(syslog, jobname):
|
||||
"""Extract job number from $HASP100 message"""
|
||||
# Pattern: /12.28.02 JOB 257 $HASP100 SIMPLE2 ON READER1
|
||||
pattern = rf'/\d+\.\d+\.\d+\s+JOB\s+(\d+)\s+\$HASP100\s+{jobname}\s+ON\s+READER'
|
||||
match = re.search(pattern, syslog, re.IGNORECASE)
|
||||
if match:
|
||||
return match.group(1)
|
||||
return None
|
||||
|
||||
def check_job_completed(syslog, jobname, job_number):
|
||||
"""Check if a job has completed printing (HASP150 message)"""
|
||||
# Pattern: /12.28.03 JOB 257 $HASP150 SIMPLE2 ON PRINTER1
|
||||
pattern = rf'/\d+\.\d+\.\d+\s+JOB\s+{job_number}\s+\$HASP150\s+{jobname}\s+ON\s+PRINTER'
|
||||
return re.search(pattern, syslog, re.IGNORECASE) is not None
|
||||
|
||||
def list_pdfs_local(local_dir):
|
||||
"""List PDF files in a local directory (for mounted volumes)"""
|
||||
import glob
|
||||
pdf_files = glob.glob(f"{local_dir}/v1403-*.pdf")
|
||||
# Sort by modification time, newest first
|
||||
pdf_files.sort(key=os.path.getmtime, reverse=True)
|
||||
return pdf_files
|
||||
|
||||
def list_pdfs_remote():
|
||||
"""List PDF files on remote Linode via SSH"""
|
||||
cmd = f"ssh {LINODE_HOST} ls -t {LINODE_PRINTOUT_DIR}/v1403-*.pdf"
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
shell=True,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
return result.stdout.strip().split('\n')
|
||||
except subprocess.CalledProcessError:
|
||||
return []
|
||||
|
||||
def find_pdf_for_job(job_number, jname, local_printout_dir=None):
|
||||
"""Find the PDF matching job number and name"""
|
||||
pattern = f"v1403-J{job_number}_{jname}-"
|
||||
|
||||
# Try the local directory first (for mounted volumes in CI)
|
||||
if local_printout_dir and os.path.isdir(str(local_printout_dir)):
|
||||
pdfs = list_pdfs_local(local_printout_dir)
|
||||
for pdf_path in pdfs:
|
||||
if pattern in pdf_path:
|
||||
return pdf_path
|
||||
return None
|
||||
|
||||
# Fall back to remote SSH access
|
||||
pdfs = list_pdfs_remote()
|
||||
for pdf_path in pdfs:
|
||||
if pattern in pdf_path:
|
||||
return pdf_path
|
||||
return None
|
||||
|
||||
def retrieve_pdf(source_path, local_filename, is_local=False):
|
||||
"""Retrieve PDF either locally (copy) or remotely (SCP)"""
|
||||
try:
|
||||
if is_local:
|
||||
# Local copy from mounted volume
|
||||
import shutil
|
||||
shutil.copy2(source_path, local_filename)
|
||||
print(f"Copied: {local_filename}")
|
||||
else:
|
||||
# Remote SCP
|
||||
cmd = f"scp {LINODE_HOST}:{source_path} {local_filename}"
|
||||
subprocess.run(cmd, shell=True, check=True)
|
||||
print(f"Retrieved: {local_filename}")
|
||||
return True
|
||||
except (subprocess.CalledProcessError, IOError) as e:
|
||||
print(f"Failed to retrieve PDF: {e}")
|
||||
return False
|
||||
|
||||
def poll_for_job(jn, to=300, poll_interval=5):
|
||||
"""Poll the console for job completion and retrieve PDF"""
|
||||
jobname_upper = jn.upper()
|
||||
start_time = time.time()
|
||||
job_number = None
|
||||
|
||||
print(f"Polling for job: {jobname_upper}")
|
||||
print(f"Timeout: {to}s, Poll interval: {poll_interval}s")
|
||||
print()
|
||||
|
||||
# Phase 1: Find a job number
|
||||
print("Phase 1: Looking for job submission ($HASP100)...")
|
||||
while time.time() - start_time < to:
|
||||
syslog = get_syslog()
|
||||
if not syslog:
|
||||
time.sleep(poll_interval)
|
||||
continue
|
||||
|
||||
job_number = find_job_number(syslog, jobname_upper)
|
||||
if job_number:
|
||||
print(f"Found job number: J{job_number}")
|
||||
break
|
||||
|
||||
time.sleep(poll_interval)
|
||||
|
||||
if not job_number:
|
||||
print(f"Timeout: Job {jobname_upper} not found in console after {to}s")
|
||||
return 1
|
||||
|
||||
# Phase 2: Wait for completion
|
||||
print(f"Phase 2: Waiting for job completion ($HASP150)...")
|
||||
while time.time() - start_time < to:
|
||||
syslog = get_syslog()
|
||||
if not syslog:
|
||||
time.sleep(poll_interval)
|
||||
continue
|
||||
|
||||
if check_job_completed(syslog, jobname_upper, job_number):
|
||||
print(f"Job J{job_number} completed and printed!")
|
||||
break
|
||||
|
||||
time.sleep(poll_interval)
|
||||
else:
|
||||
print(f"Timeout: Job J{job_number} did not complete after {to}s")
|
||||
return 1
|
||||
|
||||
# Phase 3: Retrieve PDF
|
||||
print("Phase 3: Retrieving PDF...")
|
||||
# Give the PDF a moment to be written to disk
|
||||
time.sleep(2)
|
||||
|
||||
# Check for local mounted directory (CI environment)
|
||||
local_printout_dir = os.environ.get("LOCAL_PRINTOUT_DIR")
|
||||
is_local = local_printout_dir and os.path.isdir(local_printout_dir)
|
||||
|
||||
if is_local:
|
||||
print(f"Using local mounted directory: {local_printout_dir}")
|
||||
|
||||
pdf_path = find_pdf_for_job(job_number, jobname_upper, local_printout_dir)
|
||||
if not pdf_path:
|
||||
print(f"Error: PDF not found for J{job_number}_{jobname_upper}")
|
||||
return 1
|
||||
|
||||
local_filename = f"{jobname_upper}_J{job_number}.pdf"
|
||||
if retrieve_pdf(pdf_path, local_filename, is_local):
|
||||
print(f"Success! Job output saved to: {local_filename}")
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: poll_job.py <jobname> [timeout_seconds]")
|
||||
print()
|
||||
print("Arguments:")
|
||||
print(" jobname - Job name to poll for (required)")
|
||||
print(" timeout_seconds - Maximum time to wait (optional, default: 300)")
|
||||
print()
|
||||
print("Example:")
|
||||
print(" poll_job.py SIMPLE2")
|
||||
print(" poll_job.py SIMPLE2 600")
|
||||
sys.exit(1)
|
||||
|
||||
jobname = sys.argv[1]
|
||||
timeout = int(sys.argv[2]) if len(sys.argv) > 2 else 300
|
||||
|
||||
sys.exit(poll_for_job(jobname, timeout))
|
||||
Loading…
Reference in New Issue
Block a user