def rating(request): urlparts = request.matchdict['params'] if len(urlparts) != 2: return HTTPClientError() ampy = initAmpy(request) if ampy is None: print("Failed to start ampy while creating event rating modal") return None eventid = urlparts[0] streamid = urlparts[1] # TODO grab some descriptive information that we can display about # this modal evdeets = ampy.get_single_event(streamid, eventid) request.override_renderer = "../templates/modals/eventrating.pt" return { "title": "Provide Feedback on this Event", "evstreamlabel": "TODO", "description": evdeets['description'], "eventid": eventid, "streamid": streamid, }
def update_user(request): # get the username that we are trying to update username = urllib.parse.unquote(request.matchdict["username"]) # ensure that the person making the request is the same as the user to be # updated, or someone with permissions to make changes to any user global_permissions = has_permission("editusers", request.context, request) local_permissions = (username == authenticated_userid(request)) if not global_permissions and not local_permissions: return HTTPForbidden() ampy = initAmpy(request) if ampy is None: return HTTPInternalServerError() try: body = request.json_body longname = body["longname"] email = body["email"] roles = body["roles"] if global_permissions else None password = body["password"] except (ValueError, KeyError): return HTTPBadRequest(body=json.dumps({"error": "missing value"})) if ampy.update_user(username, longname, email, roles, password): return HTTPNoContent() return HTTPBadRequest()
def display_member_modal(request, ampname, category): """ Generate the content for the mesh membership modification modal """ # TODO make sure all templates are in sensible places request.override_renderer = "../templates/member.pt" ampy = initAmpy(request) if ampy is None: print("Error starting ampy during mesh membership request") return None ampname = urllib.parse.unquote(ampname) if category == "site": members = ampy.get_meshes(None, site=ampname) available = ampy.get_meshes(None) else: members = get_mesh_members(ampy, ampname) available = ampy.get_amp_sites() # XXX exclude members return { "title": "Modify mesh membership", "ampname": ampname, "urlname": escapeURIComponent(ampname), "category": category, "members": members, "available": available, # XXX exclude members }
def modify_schedule(request): ampy = initAmpy(request) if ampy is None: return HTTPInternalServerError() try: body = request.json_body except (ValueError, KeyError): return HTTPBadRequest(body=json.dumps({"error": "invalid body"})) if len(set(SCHEDULE_OPTIONS).intersection(body)) == 0: return HTTPBadRequest(body=json.dumps( {"error": "No valid options to update"})) # if args is present then the test name also needs to be set to validate if "args" in body: if "test" not in body: return HTTPBadRequest(body=json.dumps( {"error":"Missing test type"})) if not validate_args(body["test"], body["args"]): return HTTPBadRequest(body=json.dumps( {"error": "Bad arguments %s" % body["args"]})) result = ampy.update_amp_test(request.matchdict["schedule_id"], body) if result is None: return HTTPInternalServerError() if result: return HTTPNoContent() return HTTPNotFound()
def display_modify_modal(request, username): """ Generate the content for the modal modify site/mesh page """ request.override_renderer = "../templates/user_modal.pt" ampy = initAmpy(request) if ampy is None: print("Error starting ampy during item request") return None user = ampy.get_user(urllib.parse.unquote(username)) # Disable parts of the interface that the user isn't able to change. # Global admin permissions are checked in the backend too, though the # backend will currently allow a user to delete themselves or for admins # to remove their own roles. if has_permission("editusers", request.context, request) and \ user["username"] != request.authenticated_userid: full_edit = True else: full_edit = False return { "title": "Modify user", "user": user, "full_edit": full_edit, }
def update_item(request): ampy = initAmpy(request) if ampy is None: return HTTPInternalServerError() try: body = request.json_body longname = body["longname"] description = body["description"] if request.matched_route.name == "onesite": location = body["location"] elif request.matched_route.name == "onemesh": public = body["public"] issource = body["issource"] except (ValueError, KeyError): return HTTPBadRequest(body=json.dumps({"error": "missing value"})) if request.matched_route.name == "onesite": if ampy.update_amp_site( urllib.parse.unquote(request.matchdict["name"]), longname, location, description): return HTTPNoContent() elif request.matched_route.name == "onemesh": if ampy.update_amp_mesh( urllib.parse.unquote(request.matchdict["mesh"]), longname, description, public, issource): return HTTPNoContent() return HTTPBadRequest()
def display_add_modal(request, ampname): """ Generate the content for the modal schedule page """ request.override_renderer = "../templates/schedule/modal.pt" ampy = initAmpy(request) if ampy is None: print("Error starting ampy during schedule request") return None # Try to determine site vs mesh without an explicit argument. If we are # displaying a modal without coming from a mesh or site page then something # has gone pretty wrong. if request.referer.split("/")[-3] == "sites": info = ampy.get_amp_site_info(ampname) category = "site" else: info = ampy.get_amp_mesh_info(ampname) category = "mesh" mesh_targets = ampy.get_meshes("destination") mesh_sources = ampy.get_meshes("source", site=ampname) single_targets = ampy.get_amp_sites() test_macros = get_test_macros() return { "title": "Schedule new test", "ampname": ampname, "category": category, "info": info, "mesh_sources": mesh_sources, "mesh_targets": mesh_targets, "single_targets": single_targets, "test_macros": test_macros, }
def display_site_landing(request): """ Display a list of all the available sites """ page_renderer = get_renderer("../templates/site_landing.pt") body = page_renderer.implementation().macros['body'] SCRIPTS = getCommonScripts() + [ "modals/modal.js", "modals/iteminfo_modal.js", ] ampy = initAmpy(request) if ampy is None: print("Error starting ampy during item request") return None # get all sites that are in a source mesh, or that have a test scheduled # with them as the sources sources = ampy.get_amp_sources() sourcenames = [x["ampname"] for x in sources] sources.extend([x for x in ampy.get_amp_site_endpoints() if x["ampname"] not in sourcenames]) sources.sort(key=lambda x: (x["longname"], x["ampname"])) # get all sites that are in a destination mesh, or are not in a mesh destinations = ampy.get_amp_destinations() destinations.extend(ampy.get_amp_meshless_sites()) destinations.sort(key=lambda x: (x["longname"], x["ampname"])) # exclude any sources from the destinations list sourcenames = [x["ampname"] for x in sources] destinations = [x for x in destinations if x["ampname"] not in sourcenames] banopts = getBannerOptions(request) # TODO can this be done automatically when generating the lists? for source in sources: source["urlname"] = escapeURIComponent(source["ampname"]) for destination in destinations: destination["urlname"] = escapeURIComponent(destination["ampname"]) return { "title": "AMP Measurement Sites", "body": body, "scripts": SCRIPTS, "styles": ["bootstrap.min.css"], "sources": sources, "destinations": destinations, "gtag": getGATrackingID(request), "show_dash": banopts['showdash'], "show_matrix": banopts['showmatrix'], "can_edit": has_permission("editconfig", request.context, request), "show_config": has_permission("viewconfig", request.context, request), "show_users": has_permission("editusers", request.context, request), "logged_in": authenticated_userid(request), "bannertitle": banopts['title'], }
def remove_member(request): ampy = initAmpy(request) if ampy is None: return HTTPInternalServerError() if ampy.delete_amp_mesh_member( urllib.parse.unquote(request.matchdict["mesh"]), urllib.parse.unquote(request.matchdict["name"])): return HTTPNoContent() return HTTPNotFound()
def get_single_schedule(request): ampy = initAmpy(request) if ampy is None: return HTTPInternalServerError() item = ampy.get_amp_schedule_by_id(request.matchdict["schedule_id"]) if item is None: return HTTPInternalServerError() if len(item) == 0: return HTTPNotFound() return HTTPOk(body=json.dumps(item))
def display_modify_modal(request, ampname, schedule_id): """ Generate the content for the modal modify schedule page """ request.override_renderer = "../templates/schedule/modal.pt" ampy = initAmpy(request) if ampy is None: print("Error starting ampy during schedule request") return None # Try to determine site vs mesh without an explicit argument. If we are # displaying a modal without coming from a mesh or site page then something # has gone pretty wrong. # XXX using the referer is not the best way to do this! we have to unquote # twice because everything gets encoded twice to beat WSGI current = urllib.parse.unquote( urllib.parse.unquote(request.referer.split("/")[-1])) if request.referer.split("/")[-3] == "sites": if ampname == current: # viewing a local site schedule info = ampy.get_amp_site_info(ampname) category = "site" inherited = False else: # viewing a schedule inherited from a mesh info = ampy.get_amp_mesh_info(ampname) category = "mesh" inherited = True else: # viewing a mesh schedule from the mesh itself info = ampy.get_amp_mesh_info(ampname) category = "mesh" inherited = False mesh_targets = ampy.get_meshes("destination") single_targets = ampy.get_amp_sites() sched = ampy.get_amp_source_schedule(ampname, schedule_id)[0] # dump as json to escape backslashes and quotes sched["args"] = json.dumps(sched["args"]) test_macros = get_test_macros() return { "title": "Modify scheduled test", "ampname": ampname, "info": info, "inherited": inherited, "category": category, "mesh_targets": mesh_targets, "single_targets": single_targets, "schedule": sched, "test_macros": test_macros, "mesh_sources": [], }
def delete_user(request): # TODO forcibly logout and revoke credentials of user if they are logged in ampy = initAmpy(request) if ampy is None: return HTTPInternalServerError() result = ampy.delete_user(urllib.parse.unquote(request.matchdict["username"])) if result is None: return HTTPInternalServerError() if result: return HTTPNoContent() return HTTPNotFound()
def check_login(request, username, password): if username is None or password is None or len(password) == 0: return False ampy = initAmpy(request) if ampy is None: print("Failed to start ampy for checking login details") return False user = ampy.get_user(username) if user and user["enabled"]: return check_password(password, user["password"]) return False
def delete_schedule(request): """ Delete the specified schedule test item """ ampy = initAmpy(request) if ampy is None: return HTTPInternalServerError() result = ampy.delete_amp_test(request.matchdict["schedule_id"]) if result is None: return HTTPInternalServerError() if result: return HTTPNoContent() return HTTPNotFound()
def groupfinder(username, request): if username is None: return None ampy = initAmpy(request) if ampy is None: print("Failed to start ampy for checking user group details") return None user = ampy.get_user(username) if user: return ["g:%s" % g for g in user.get("roles", [])] return None
def get_flagged_mesh_tests(request): ampy = initAmpy(request) if ampy is None: return HTTPInternalServerError() mesh = urllib.parse.unquote(request.matchdict["mesh"]) tests = ampy.get_flagged_mesh_tests(mesh) if tests is None: return HTTPInternalServerError() if tests is False: return HTTPNotFound() return HTTPOk(body=json.dumps({"tests": tests}))
def delete_endpoint(request): ampy = initAmpy(request) if ampy is None: return HTTPInternalServerError() result = ampy.delete_amp_test_endpoints(request.matchdict["schedule_id"], urllib.parse.unquote(request.matchdict["name"]), urllib.parse.unquote(request.matchdict["destination"])) if result is None: return HTTPInternalServerError() if result: return HTTPNoContent() return HTTPNotFound()
def schedule_status(request): """ Get the enabled status of the specified schedule test item """ ampy = initAmpy(request) if ampy is None: return HTTPInternalServerError() status = ampy.is_amp_test_enabled(request.matchdict["schedule_id"]) if status is None: response = HTTPNotFound() else: if status: response = HTTPOk(body=json.dumps({"status": "enabled"})) else: response = HTTPOk(body=json.dumps({"status": "disabled"})) return response
def get_destinations(request): ampy = initAmpy(request) if ampy is None: return HTTPInternalServerError() item = ampy.get_amp_schedule_by_id(request.matchdict["schedule_id"]) if item is None: return HTTPInternalServerError() if len(item) == 0: return HTTPNotFound() sites = item["dest_site"] if "dest_site" in item else [] meshes = item["dest_mesh"] if "dest_mesh" in item else [] return HTTPOk(body=json.dumps({"dest_sites": sites, "dest_meshes": meshes}))
def get_all_items(request): ampy = initAmpy(request) if ampy is None: return HTTPInternalServerError() if request.matched_route.name == "allsites": items = ampy.get_amp_sites() label = "sites" elif request.matched_route.name == "allmeshes": items = ampy.get_meshes(None) label = "meshes" if items is None: return HTTPInternalServerError() return HTTPOk(body=json.dumps({label: items}))
def api(request): """ Determine which API a request is being made against and fetch data """ urlparts = request.matchdict['params'] # Dictionary of possible internal API methods we support apidict = { #'_tracemap': tracemap, } ampyapidict = { '_view': viewapi.graph, '_legend': viewapi.legend, '_createview': viewapi.create, '_destinations': viewapi.destinations, '_event': eventapi.event, '_matrix': matrixapi.matrix, '_matrix_axis': matrixapi.matrix_axis, '_matrix_mesh': matrixapi.matrix_mesh, '_tooltip': tooltipapi.tooltip, '_validatetab': viewapi.validatetab, } # /api/_* are private APIs # /api/* is the public APIs that looks similar to the old one if len(urlparts) > 0: interface = urlparts[0] if interface.startswith("_"): if interface in ampyapidict: ampy = initAmpy(request) if ampy is None: print("Failed to start ampy!") return None result = ampyapidict[interface](ampy, request) # Allow responses for certain API calls to be cached for 2 mins if request.registry.settings['prevent_http_cache'] is not True: if interface in ['_view']: request.response.cache_expires = 120 return result elif interface in apidict: return apidict[interface](request) else: return {"error": "Unsupported API method"} return public(request)
def delete_item(request): ampy = initAmpy(request) if ampy is None: return HTTPInternalServerError() if request.matched_route.name == "onesite": result = ampy.delete_amp_site( urllib.parse.unquote(request.matchdict["name"])) elif request.matched_route.name == "onemesh": result = ampy.delete_amp_mesh( urllib.parse.unquote(request.matchdict["mesh"])) if result is None: return HTTPInternalServerError() if result: return HTTPNoContent() return HTTPNotFound()
def create_user(request): ampy = initAmpy(request) if ampy is None: return HTTPInternalServerError() try: body = request.json_body username = body["username"] longname = body["longname"] email = body["email"] roles = body["roles"] password = body["password"] except (ValueError, KeyError): return HTTPBadRequest(body=json.dumps({"error": "missing value"})) if ampy.add_user(username, longname, email, roles, password): return HTTPNoContent() return HTTPBadRequest()
def create_item(request): ampy = initAmpy(request) if ampy is None: return HTTPInternalServerError() try: body = request.json_body ampname = body["ampname"] longname = body["longname"] description = body["description"] if request.matched_route.name == "allsites": location = body["location"] elif request.matched_route.name == "allmeshes": public = body["public"] issource = body["issource"] except (ValueError, KeyError): return HTTPBadRequest(body=json.dumps({"error": "missing value"})) if re.search("[^.:/a-zA-Z0-9_-]", ampname) is not None: return HTTPBadRequest( body=json.dumps({"error": "bad characters in ampname"})) if request.matched_route.name == "allsites": result = ampy.add_amp_site(ampname, longname, location, description) url = request.route_url("onesite", name=escapeURIComponent(ampname)) label = "site" elif request.matched_route.name == "allmeshes": result = ampy.add_amp_mesh(ampname, longname, description, public, issource) url = request.route_url("onemesh", mesh=escapeURIComponent(ampname)) label = "mesh" else: return HTTPBadRequest() if result: return HTTPCreated(headers=[("Location", url)], body=json.dumps( {label: { "ampname": ampname, "url": url, }})) return HTTPBadRequest()
def flag_mesh_tests(request): # XXX is it a good idea to be doing more than one operation at once here? # should the calling mesh info modal make a request per change? ampy = initAmpy(request) if ampy is None: return HTTPInternalServerError() mesh = urllib.parse.unquote(request.matchdict["mesh"]) try: body = request.json_body tests = body["tests"] except (ValueError, KeyError): return HTTPBadRequest(body=json.dumps({"error": "missing tests"})) current = ampy.get_flagged_mesh_tests(mesh) # return error before we do any modifications for test in tests: if test not in [ "latency", "tput", "hops", "http", "youtube", "external", "sip" ]: return HTTPBadRequest(body=json.dumps({"error": "unknown test"})) # add new tests that aren't currently enabled for test in tests: if test not in current: result = ampy.flag_mesh_test(mesh, test) if result is None: return HTTPInternalServerError() if result is False: return HTTPNotFound() # remove old tests that should no longer be enabled for test in current: if test not in tests: result = ampy.unflag_mesh_test(mesh, test) if result is None: return HTTPInternalServerError() if result is False: return HTTPNotFound() return HTTPNoContent()
def add_endpoint(request): ampy = initAmpy(request) if ampy is None: return HTTPInternalServerError() try: body = request.json_body destination = body["destination"] except (ValueError, KeyError): return HTTPBadRequest(body=json.dumps({"error": "missing destination"})) result = ampy.add_amp_test_endpoints(request.matchdict["schedule_id"], urllib.parse.unquote(request.matchdict["name"]), destination) if result is None: return HTTPInternalServerError() if result: return HTTPNoContent() return HTTPBadRequest()
def matrix(request): page_renderer = get_renderer("../templates/matrix.pt") body = page_renderer.implementation().macros['body'] SCRIPTS = getCommonScripts() + [ "lib/jquery.sparkline.min.js", "pages/matrix.js", "matrix/basematrix.js", "matrix/latencymatrix.js", "matrix/lossmatrix.js", "matrix/hopmatrix.js", "matrix/throughputmatrix.js", "matrix/httpmatrix.js", "matrix/youtubematrix.js", "matrix/sipmatrix.js", ] ampy = initAmpy(request) if ampy is None: print("Error starting ampy during matrix request") return None src = ampy.get_meshes("source", public=True) banopts = getBannerOptions(request) return { "title": "AMP Measurements", "page": "matrix", "body": body, "scripts": SCRIPTS, "styles": ['bootstrap.min.css'], "logged_in": authenticated_userid(request), "show_config": has_permission("viewconfig", request.context, request), "show_users": has_permission("editusers", request.context, request), "gtag": getGATrackingID(request), "show_dash": banopts['showdash'], "show_matrix": banopts['showmatrix'], "bannertitle": banopts['title'], "srcMeshes": src, "tabs": _create_tabs(request), }
def get_item(request): ampy = initAmpy(request) if ampy is None: return HTTPInternalServerError() if request.matched_route.name == "onesite": item = ampy.get_amp_site_info( urllib.parse.unquote(request.matchdict["name"])) label = "site" elif request.matched_route.name == "onemesh": item = ampy.get_amp_mesh_info( urllib.parse.unquote(request.matchdict["mesh"])) label = "mesh" if item is None: return HTTPInternalServerError() if "unknown" in item and item["unknown"] is True: return HTTPNotFound() return HTTPOk(body=json.dumps({label: item}))
def get_members(request): ampy = initAmpy(request) if ampy is None: return HTTPInternalServerError() if request.matched_route.name == "sitemeshes": members = ampy.get_meshes(None, site=urllib.parse.unquote( request.matchdict["name"])) elif request.matched_route.name == "meshsites": # TODO using this function is not ideal, could be done better in ampy members = get_mesh_members( ampy, urllib.parse.unquote(request.matchdict["mesh"])) # TODO deal with not existing vs zero mesh membership if members is None: return HTTPInternalServerError() if members is False: return HTTPNotFound() return HTTPOk(body=json.dumps({"membership": members}))
def eventview(request): start = None end = None # extract the stream id etc from the request so we can rebuild it urlparts = request.matchdict["params"] if len(urlparts) < 2: raise exception_response(404) basestyle = urlparts[0] stream = int(urlparts[1]) if len(urlparts) > 2: start = urlparts[2] if len(urlparts) > 3: end = urlparts[3] collection = graphStyleToCollection(basestyle) graphstyle = collectionToGraphStyle(basestyle) ampy = initAmpy(request) if ampy is None: print("Failed to start ampy for generating event view") return None # convert it into a view id, creating it if required view_id = ampy.get_event_view(collection, stream) if view_id is None: print("Failed to generate view for event on stream %d" % (stream)) return None # call the normal graphing function with the view id params = (graphstyle, str(view_id)) if start: params += (start,) if end: params += (end,) newurl = request.route_url("view", params=params) # send an HTTP 301 and browsers should remember the new location return HTTPMovedPermanently(location=newurl)