import threading import subprocess import hashlib import time from datetime import datetime, timezone from opengnsys import operations from opengnsys.log import logger def job_readstdout(job): for l in iter(job['p'].stdout.readline, b''): job['stdout'] += l.decode ('utf-8', 'ignore') def job_readstderr(job): for l in iter(job['p'].stderr.readline, b''): job['stderr'] += l.decode ('utf-8', 'ignore') class JobMgr(): jobs = {} def launch_job(self, script, is_client): logger.debug ('in launch_job(), is_client "{}"'.format(is_client)) args = operations.build_popen_args (script) logger.debug ('args "{}"'.format (args)) now = datetime.now (tz=timezone.utc) ts = now.strftime ('%Y-%m-%d %H:%M:%S.%f%z') ## '%s' doesn't work on windows jobid = hashlib.sha256 (now.isoformat().encode('UTF-8') + script.encode ('UTF-8')).hexdigest()[0:12] p = subprocess.Popen (args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) self.jobs[jobid] = { 'p': p, 'pid': p.pid, 'starttime': ts, 'script': script, 'client': is_client, 'status': 'running', 'stdout': '', 'stderr': '' } self.jobs[jobid]['t1'] = threading.Thread (target=job_readstdout, args=(self.jobs[jobid],)) self.jobs[jobid]['t2'] = threading.Thread (target=job_readstderr, args=(self.jobs[jobid],)) self.jobs[jobid]['t1'].start() self.jobs[jobid]['t2'].start() logger.debug ('jobs "{}"'.format (self.jobs)) return jobid def prepare_jobs(self): ## can't return self.jobs because the Popen object at self.jobs[id]['p'] is not serializable. So, need to create a new dict to return st = [] for jobid in self.jobs: j = self.jobs[jobid] entry = dict ((k, j[k]) for k in ['pid', 'starttime', 'script', 'client', 'status', 'stdout', 'stderr']) entry['jobid'] = jobid if j['p'].poll() is not None: ## process finished entry['rc'] = j['p'].returncode entry['status'] = 'finished' st.append (entry) return st def terminate_job(self, jobid): if jobid not in self.jobs: return {} p = self.jobs[jobid]['p'] p.terminate() time.sleep (1) if p.poll() is not None: return { 'terminated': True } p.kill() time.sleep (1) if p.poll() is not None: return { 'killed': True } return { 'killed': False }