1""" 2Functions for creating and working with job IDs 3""" 4 5import datetime 6import hashlib 7import os 8from calendar import month_abbr as months 9 10import salt.utils.stringutils 11 12LAST_JID_DATETIME = None 13 14 15def _utc_now(): 16 """ 17 Helper method so tests do not have to patch the built-in method. 18 """ 19 return datetime.datetime.utcnow() 20 21 22def gen_jid(opts): 23 """ 24 Generate a jid 25 """ 26 global LAST_JID_DATETIME # pylint: disable=global-statement 27 28 jid_dt = _utc_now() 29 if not opts.get("unique_jid", False): 30 return "{:%Y%m%d%H%M%S%f}".format(jid_dt) 31 if LAST_JID_DATETIME and LAST_JID_DATETIME >= jid_dt: 32 jid_dt = LAST_JID_DATETIME + datetime.timedelta(microseconds=1) 33 LAST_JID_DATETIME = jid_dt 34 return "{:%Y%m%d%H%M%S%f}_{}".format(jid_dt, os.getpid()) 35 36 37def is_jid(jid): 38 """ 39 Returns True if the passed in value is a job id 40 """ 41 if not isinstance(jid, str): 42 return False 43 if len(jid) != 20 and (len(jid) <= 21 or jid[20] != "_"): 44 return False 45 try: 46 int(jid[:20]) 47 return True 48 except ValueError: 49 return False 50 51 52def jid_to_time(jid): 53 """ 54 Convert a salt job id into the time when the job was invoked 55 """ 56 jid = str(jid) 57 if len(jid) != 20 and (len(jid) <= 21 or jid[20] != "_"): 58 return "" 59 year = jid[:4] 60 month = jid[4:6] 61 day = jid[6:8] 62 hour = jid[8:10] 63 minute = jid[10:12] 64 second = jid[12:14] 65 micro = jid[14:20] 66 67 ret = "{}, {} {} {}:{}:{}.{}".format( 68 year, months[int(month)], day, hour, minute, second, micro 69 ) 70 return ret 71 72 73def format_job_instance(job): 74 """ 75 Format the job instance correctly 76 """ 77 ret = { 78 "Function": job.get("fun", "unknown-function"), 79 "Arguments": list(job.get("arg", [])), 80 # unlikely but safeguard from invalid returns 81 "Target": job.get("tgt", "unknown-target"), 82 "Target-type": job.get("tgt_type", "list"), 83 "User": job.get("user", "root"), 84 } 85 86 if "metadata" in job: 87 ret["Metadata"] = job.get("metadata", {}) 88 else: 89 if "kwargs" in job: 90 if "metadata" in job["kwargs"]: 91 ret["Metadata"] = job["kwargs"].get("metadata", {}) 92 return ret 93 94 95def format_jid_instance(jid, job): 96 """ 97 Format the jid correctly 98 """ 99 ret = format_job_instance(job) 100 ret.update({"StartTime": jid_to_time(jid)}) 101 return ret 102 103 104def format_jid_instance_ext(jid, job): 105 """ 106 Format the jid correctly with jid included 107 """ 108 ret = format_job_instance(job) 109 ret.update({"JID": jid, "StartTime": jid_to_time(jid)}) 110 return ret 111 112 113def jid_dir(jid, job_dir=None, hash_type="sha256"): 114 """ 115 Return the jid_dir for the given job id 116 """ 117 if not isinstance(jid, str): 118 jid = str(jid) 119 jhash = getattr(hashlib, hash_type)( 120 salt.utils.stringutils.to_bytes(jid) 121 ).hexdigest() 122 123 parts = [] 124 if job_dir is not None: 125 parts.append(job_dir) 126 parts.extend([jhash[:2], jhash[2:]]) 127 return os.path.join(*parts) 128