def installOrUpdateStack(self, options): """ installs a stack if it doesn't exist or updates it param options: as for the buildStackDict function above returns stack status or None """ try: status = self.waitForStack(options["stackname"]) xd = self.buildStackDict(options) if status is not None and "COMPLETE" in status: if not self.checkStack(self.stackDetails(options["stackname"])): # stack exists, so update it log.warning(f"""updating stack {options["stackname"]}""") self.updateStack(**xd) time.sleep(10) status = self.waitForStack(options["stackname"]) else: log.info(f"""Stack {options["stackname"]} is up to date""") elif status is None: log.info(f"""creating stack {options["stackname"]}""") self.createStack(**xd) time.sleep(10) status = self.waitForStack(options["stackname"]) else: msg = f"""stack {options["stackname"]} is status: {status}""" log.warning(msg) raise Exception(msg) except ClientError as ce: log.warning("Client Error: stack probably already up to date") log.warning(f"{ce}") except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def waitForStack(self, stackn, timeout=10, sleeptime=30): """ wait for a stack to become "..._COMPLETE" waits for timeout * sleeptime seconds returns the stack status """ try: status = None cn = 0 sleeptime = sleeptime while True: cn += 1 if cn > timeout: log.error( f"Timeout expired waiting for stack {stackn} to become ready" ) break status = self.stackStatus(stackn) if status is not None and "COMPLETE" in status: log.info(f"Stack {stackn} is {status}") break elif status is None: log.warning(f"stack {stackn} does not exist (anymore)") break time.sleep(sleeptime) return status except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def stackStatus(self, stackname): try: stack = self.stackDetails(stackname) return stack["StackStatus"] if stack is not None else None except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def addToString(xstr, xadd): """Appends the string `xadd` to the string `xstr`. if xadd is a list then each list member that is a string is appended to xstr Args: xstr: str input string xadd: list or str Raises: TypeError: Exception Returns: str: """ try: if type(xstr) is str: op = xstr else: op = "" if type(xadd) is list: for xi in xadd: if type(xi) is str: op += xi elif type(xadd) is str: op += xadd else: raise TypeError( "Input format error. xadd is neither list nor string") return op except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def displayValue(val, label, zero=True): """Pluralises the label. if zero is True val == 0 then the empty string is returned. Args: val: number label: str zero: Bool Raises: TypeError: Exception if val is not numeric Returns: str: """ try: if zero and val == 0: return "" dlabel = label if val == 1 else label + "s" sval = str(val) if not sval.isnumeric(): raise TypeError("input is not numeric") return addToString(sval, [" ", dlabel]) except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def secondsFromHMS(shms): """Convert "01:02:32.47" to seconds. Args: shms: str input string seperated by colons Returns: int: number of seconds represented by input string """ try: hrs = mins = secs = extra = 0 xtmp = shms.split(".") if int(xtmp[1]) > 50: extra = 1 tmp = xtmp[0].split(":") cn = len(tmp) if cn == 3: hrs = int(tmp[0]) mins = int(tmp[1]) secs = int(tmp[2]) elif cn == 2: mins = int(tmp[0]) secs = int(tmp[1]) else: secs = int(tmp[0]) return (hrs * 3600) + (mins * 60) + secs + extra except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def findInstances(self, instlist): """ find instances in instlist instlist is a list of instance-id strings """ try: instances = [] kwargs = {"InstanceIds": instlist} if type(instlist) is list else {} while True: try: # will raise client error if instances don't exist resp = self.client.describe_instances(**kwargs) try: rinsts = [ i["Instances"] for i in [ r for r in resp["Reservations"] if "Instances" in r ] ] instances += [i for subi in rinsts for i in subi] kwargs["NextToken"] = resp["NextToken"] except KeyError: break except ClientError as ce: log.debug(f"ClientError: Instances probably don't exist: {ce}") break return instances except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def delimitString(xstr, delimeter=" - "): """Delimits the string with the delimiter. Args: xstr: str or list delimeter: str Raises: ValueError: Exception Returns: str: """ try: op = "" xlist = None if type(xstr) is str: xlist = xstr.split(" ") elif type(xstr) is list: xlist = xstr if xlist is None: raise ValueError("delimitString: parameter must be string or list") for xl in xlist: if len(op) > 0: op += delimeter + xl else: op = xl return op except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def getMatchingInstances(self, instlst=None): """ find the instances named in the `instlst` list or all instances if `instlst` is None NOTE: this does not appear to work with filters, use the 'findInstances' function above """ try: instances = [] kwargs = {} if type(instlst) is list: kwargs["Filters"] = [{"Name": "instance-id", "Values": instlst}] while True: resp = self.client.describe_instances(**kwargs) try: rinsts = [ i["Instances"] for i in [r for r in resp["Reservations"] if "Instances" in r] ] instances += [i for subi in rinsts for i in subi] kwargs["NextToken"] = resp["NextToken"] except KeyError: break return instances except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def sizeof_fmt(num, suffix="B"): """Displays the size of num in human readable units. from article by Fred Cirera: https://web.archive.org/web/20111010015624/http://blogmag.net/ \ blog/read/38/Print_human_readable_file_size and stackoverflow: https://stackoverflow.com/questions/1094841/ \ reusable-library-to-get-human-readable-version-of-file-size Args: num: int a number (e.g. the size of a file) suffix: str a suffix to append to the output string Returns: str: num expressed in human readable units """ try: for unit in ["", "K", "M", "G", "T", "P", "E", "Z"]: if abs(num) < 1024.0: return f"{num:3.1f}{unit}{suffix}" num /= 1024.0 except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def getCertDetails(self, certarn): try: cert = self.client.describe_certificate(CertificateArn=certarn) return cert["Certificate"] except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def reduceTime(unit, secs): """Performs the modulo function on secs. Args: unit: int the divisor secs: int a number Raises: ValueError: Exception Returns: tuple: (units: int, remainder: int) """ try: rem = units = 0 if unit > 0: units = int(secs / unit) rem = int(secs % unit) else: raise ValueError( f"divide by zero requested in reduceTime: unit: {unit}, secs: {secs}" ) return (units, rem) except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def decomplexifyhms(tim, index, labels, labindex, oplen, colons=False): """Secondary function to remove some of the complexity of the hms function. Do not call this function directly Args: tim: list index: int labels: list labindex: int oplen: int colons: Bool Returns: list: """ try: op = [] if colons: delim = ":" else: delim = " " if labindex == 2 else ", " if index == 3: delim = " " if labindex == 2 else " and " if oplen > 0: op.append(delim) if colons: sval = padStr(str(tim[index]), pad="0") else: if labindex == 2: sval = str(tim[index]) + labels[labindex][index] else: sval = displayValue(tim[index], labels[labindex][index], zero=False) op.append(sval) return op except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def updateStack(self, **kwargs): try: resp = self.client.update_stack(**kwargs) if "StackId" in resp: return resp["StackId"] return None except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def buildStackParams(self, paramdict): """ builds a set of parameters from a paramstring """ try: lpd = self.expandDictToList( paramdict, keyname="ParameterKey", valuename="ParameterValue" ) return lpd except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def __init__(self, **kwargs): try: if kwargs is None: kwargs = {"noresource": True} else: kwargs["noresource"] = True super().__init__(**kwargs) self.newClient("acm") except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def updateCW(self): """ posts the metrics to CloudWatch """ try: for key in self.metrics: if len(self.metrics[key]) > 0: self.client.put_metric_data(Namespace=key, MetricData=self.metrics[key]) except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def makeFilePath(fqfn): """Makes the path for the file. Args: fqfn: fully-qualified file name """ try: pfn = os.path.basename(fqfn) makePath(pfn) except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def stackDetails(self, stackname): try: resp = self.client.describe_stacks(StackName=stackname) if "Stacks" in resp: stack = resp["Stacks"][0] return stack except ClientError: log.debug(f"stack: {stackname} does not exist") return None except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def fileDelete(fqfn): """Deletes the named file. Args: fqfn: - fully-qualified filename to delete """ try: if fileExists(fqfn): os.unlink(fqfn) except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def hms(secs, small=True, short=True, single=False, colons=False): """Convert `secs` to days, hours, minutes and seconds. if `small` is True then only return the higher values if they are > zero if `short` is True then the labels are their short form if `single` is True then the labels are single letters if `colons` is True then the output is of the form: 01:03:23 Args: secs: int the number of seconds small: Bool do not return day, hours or mins if they are zero short: Bool use short labels single: Bool use single letter labels colons: Bool return a string of the form 01:32:24 Returns: str: """ try: labs = [ ["day", "hour", "minute", "second"], ["day", "hour", "min", "sec"], ["d", "h", "m", "s"], ] tim = [0, 0, 0, 0] units = [60 * 60 * 24, 60 * 60, 60] rem = secs for index in range(3): tim[index], rem = reduceTime(units[index], rem) tim[3] = rem op = [] started = not small if single: cnlabs = 2 else: cnlabs = 1 if short else 0 for cn in range(4): if not started and tim[cn] > 0: started = True if started: op += decomplexifyhms(tim, cn, labs, cnlabs, len(op), colons) msg = addToString("", op) return msg except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def getCertList(self): try: pag = self.client.get_paginator("list_certificates") pageit = pag.paginate() certs = [] for page in pageit: if "CertificateSummaryList" in page: for cert in page["CertificateSummaryList"]: certs.append(cert["CertificateArn"]) return certs except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def makePath(pn): """Makes the path. Args: pn: the fully-qualified path to make """ try: if not dfExists(pn): p = Path(pn) p.mkdir(mode=0o755, parents=True, exist_ok=True) except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def absPath(fn): """Transforms the filename into a fully-qualified file name. Args: fn: file name containing possible unix filesystem 'markers' Returns: str: """ try: return os.path.abspath(os.path.expanduser(fn)) except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def getRegions(self): """ returns a list of all available regions """ try: resp = self.client.describe_regions() regions = [ region["RegionName"] for region in resp["Regions"] if "Regions" in resp ] log.debug("Regions: {}".format(regions)) return regions except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def fileExists(fqfn): """Tests for the existance of a file. Args: fqfn: str fully-qualified file name Returns: Bool: True or False """ try: return Path(fqfn).is_file() except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def dirExists(fqdn): """Tests for the existance of a directory. Args: fqdn: fully-qualified directory name Returns: Bool: True or False """ try: return Path(fqdn).is_dir() except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def fileSize(fqfn): """Retrieves the size of the file in bytes. Args: fqfn: str fully-qualified filename Returns: int: the size of the file """ try: if fileExists(fqfn): return os.stat(fqfn).st_size except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def addMetric(self, name, value): """ adds a metric and it's value into the metrics array adds a metric and it's value into the metrics array with a unit of None params: name: [required] the metric name value:[required] the metric value """ try: self.addUnitMetric(name, value, unit="None") except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)
def dfExists(fqdfn): """Tests for the existance of a directory or file. Args: fqdfn: fully-qualified directory or file name Returns: Bool: True or False """ try: ret = fileExists(fqdfn) if not ret: ret = dirExists(fqdfn) return ret except Exception as e: fname = sys._getframe().f_code.co_name errorRaise(fname, e)