1import ast 2import json 3import logging 4import os.path 5 6from flask import abort, request, url_for 7from jinja2.utils import contextfunction 8from pypuppetdb.errors import EmptyResponseError 9from requests.exceptions import ConnectionError, HTTPError 10 11 12log = logging.getLogger(__name__) 13 14 15@contextfunction 16def url_static_offline(context, value): 17 request_parts = os.path.split(os.path.dirname(context.name)) 18 static_path = '/'.join(request_parts[1:]) 19 20 return url_for('static', filename="%s/%s" % (static_path, value)) 21 22 23def url_for_field(field, value): 24 args = request.view_args.copy() 25 args.update(request.args.copy()) 26 args[field] = value 27 return url_for(request.endpoint, **args) 28 29 30def jsonprint(value): 31 return json.dumps(value, indent=2, separators=(',', ': ')) 32 33 34def get_db_version(puppetdb): 35 """ 36 Get the version of puppetdb. Version form 3.2 query 37 interface is slightly different on mbeans 38 """ 39 ver = () 40 try: 41 version = puppetdb.current_version() 42 (major, minor, build) = [int(x) for x in version.split('.')] 43 ver = (major, minor, build) 44 log.info("PuppetDB Version %d.%d.%d" % (major, minor, build)) 45 except ValueError: 46 log.error("Unable to determine version from string: '%s'" % puppetdb.current_version()) 47 ver = (4, 2, 0) 48 except HTTPError as e: 49 log.error(str(e)) 50 except ConnectionError as e: 51 log.error(str(e)) 52 except EmptyResponseError as e: 53 log.error(str(e)) 54 return ver 55 56 57def parse_python(value: str): 58 """ 59 :param value: any string, number, bool, list or a dict 60 casted to a string (f.e. "{'up': ['eth0'], (...)}") 61 :return: the same value but with a proper type 62 """ 63 try: 64 return ast.literal_eval(value) 65 except ValueError: 66 return str(value) 67 except SyntaxError: 68 return str(value) 69 70 71def formatvalue(value): 72 if isinstance(value, str): 73 return value 74 elif isinstance(value, list): 75 return ", ".join(map(formatvalue, value)) 76 elif isinstance(value, dict): 77 ret = "" 78 for k in value: 79 ret += k + " => " + formatvalue(value[k]) + ",<br/>" 80 return ret 81 else: 82 return str(value) 83 84 85def prettyprint(value): 86 html = '<table class="ui basic fixed sortable table"><thead><tr>' 87 88 # Get keys 89 for k in value[0]: 90 html += "<th>" + k + "</th>" 91 92 html += "</tr></thead><tbody>" 93 94 for e in value: 95 html += "<tr>" 96 for k in e: 97 html += "<td>" + formatvalue(e[k]) + "</td>" 98 html += "</tr>" 99 100 html += "</tbody></table>" 101 return html 102 103 104def get_or_abort(func, *args, **kwargs): 105 """Execute the function with its arguments and handle the possible 106 errors that might occur. 107 108 In this case, if we get an exception we simply abort the request. 109 """ 110 try: 111 return func(*args, **kwargs) 112 except HTTPError as e: 113 log.error(str(e)) 114 abort(e.response.status_code) 115 except ConnectionError as e: 116 log.error(str(e)) 117 abort(500) 118 except EmptyResponseError as e: 119 log.error(str(e)) 120 abort(204) 121 except Exception as e: 122 log.error(str(e)) 123 abort(500) 124 125 126def yield_or_stop(generator): 127 """Similar in intent to get_or_abort this helper will iterate over our 128 generators and handle certain errors. 129 130 Since this is also used in streaming responses where we can't just abort 131 a request we raise StopIteration. 132 """ 133 while True: 134 try: 135 yield next(generator) 136 except (EmptyResponseError, ConnectionError, HTTPError, StopIteration): 137 return 138