async def cue_the_music(request: "Request") -> "Response": if config.TRACE: logger.debug(f"{request.url}") score_name = request.match_info["score_name"] if score_name not in scores: return util.johann_response(False, f"unrecognized score '{score_name}'", 404) score = scores[score_name] # make sure score isn't already running (e.g. by another user) if score.started_at and not score.finished: return util.johann_response(False, "score already playing", 400) # make sure each Player in the Score is matched to actual entities/containers success, err_msgs = score.validate_create_host_mappings() if not success: logger.warning( f"{score.name}: tuning failed; errors validating host mappings:\n{err_msgs}" ) return util.johann_response(False, err_msgs, 400) task = asyncio.ensure_future(score.play()) task = util.wrap_future(task, score.name, None, score_or_measure=score) asyncio.ensure_future(task) return util.johann_response(True, "score is playing")
async def get_measure_status(request: "Request") -> "Response": if config.TRACE: logger.debug(f"{request.url}") measure, msg = _get_measure_helper(request) if not measure: return util.johann_response(False, msg, 404) return util.johann_response(True, [], data=measure.get_status())
async def get_score_measures(request: "Request") -> "Response": if config.TRACE: logger.debug(f"{request.url}") score_name = request.match_info["score_name"] if score_name not in scores: return util.johann_response(False, f"unrecognized score '{score_name}'", 404) score = scores[score_name] return util.johann_response(True, [], data=[x.name for x in score.measures])
async def get_host(request: "Request") -> "Response": if config.TRACE: logger.debug(f"{request.url}") host_name = request.match_info["host_name"] if host_name not in hosts: return util.johann_response(False, f"unrecognized host '{host_name}'", 404) host = hosts[host_name] return util.johann_response(True, [], data=host.dump())
async def get_score_raw(request: "Request") -> "Response": if config.TRACE: logger.debug(f"{request.url}") score_name = request.match_info["score_name"] if score_name not in scores: return util.johann_response(False, f"unrecognized score '{score_name}'", 404) score = scores[score_name] return util.johann_response( True, [], data=score.dump(exclude_local=False, yaml_fields_only=False) )
async def _retrieve_stored_data( score_name: str, key: Optional[str] = None, subkey: Optional[str] = None, subsubkey: Optional[str] = None, subsubsubkey: Optional[str] = None, ) -> "Response": if score_name not in scores: return util.johann_response(False, f"unrecognized score '{score_name}'", 404) score = scores[score_name] success, msg, code, data = score.fetch_stored_data( key, subkey, subsubkey, subsubsubkey ) logger.debug(f"retrieve_stored_data API call: {msg}") return util.johann_response(success, msg, code, data=data)
async def get_hosts(request: "Request") -> "Response": if config.TRACE: logger.debug(f"{request.url}") ret = {} for name, host in hosts.items(): ret[name] = {"name": host.name, "image": host.get_image()} return util.johann_response(True, [], data=ret)
async def api_read_score(request: "Request") -> "Response": if config.TRACE: logger.debug(f"{request.url}") score_name = request.match_info["score_name"] # check for force params = request.rel_url.query if "force" in params: if score_name not in scores: logger.warning("Force resetting a score that isn't loaded") else: del scores[score_name] score_dict, err_msg, status_code = read_score(score_name) if score_dict: return util.johann_response(True, [], data=score_dict, status_code=status_code) else: return util.johann_response(False, err_msg, status_code=status_code)
async def get_score_status_alt(request: "Request") -> "Response": if config.TRACE: logger.debug(f"{request.url}") score_name = request.match_info["score_name"] if score_name not in scores: return util.johann_response(False, f"unrecognized score '{score_name}'", 404) score = scores[score_name] status = score.get_status(short=False) ret = {} measures = {} current = 0 totals = score.get_host_totals() total = totals["total"] fails = 0 for m_name, m_status in status["measures"].items(): measure_ret = {} m_current = 0 m_fails = 0 for p_status in m_status["task_status"].values(): m_fails += p_status["failed_count"] m_current += p_status["meta"]["current"] measure_ret["total"] = totals[m_name]["total"] measure_ret["current"] = m_current measure_ret["failed_count"] = m_fails current += m_current fails += m_fails measure_ret["state"] = m_status["state"] measures[m_name] = measure_ret ret["current"] = current ret["total"] = total ret["failed_count"] = fails ret["state"] = status["state"] ret["status"] = ret["state"] ret["measures"] = measures ret["raw"] = status return util.johann_response(True, [], data=ret)
async def manually_play_measure(request: "Request") -> "Response": if config.TRACE: logger.debug(f"{request.url}") score_name = request.match_info["score_name"] measure_name = request.match_info["measure_name"] if score_name not in scores: return util.johann_response(False, f"unrecognized score '{score_name}'", 404) score = scores[score_name] measure_names = [x.name for x in score.measures] if measure_name not in measure_names: return util.johann_response( False, f"unrecognized measure '{measure_name}'", 404 ) measure = [x for x in score.measures if x.name == measure_name][0] m = measure if m.started(): if ( "force" in request.rel_url.query and request.rel_url.query["force"].lower() != "false" ): msg = f"Forcing re-play of measure '{m.name}'" logger.warning(msg) else: return util.johann_response( False, "measure already played/playing; to run anyway, include query param" " 'force=true'", 400, ) else: msg = f"Manually playing measure '{m.name}'" logger.info(msg) score.queue_measure(m) return util.johann_response(True, msg)
async def get_scores(request: "Request") -> "Response": if config.TRACE: logger.debug(f"{request.url}") ret = {} for name, score in scores.items(): if score.category not in ret: ret[score.category] = [] ret[score.category].append({"name": name, "description": score.description}) ret[score.category].sort(key=lambda t: t["name"]) ret = OrderedDict(sorted(ret.items(), key=lambda t: t[0])) # sort by category return util.johann_response(True, [], data=ret)
async def add_hosts(request: "Request") -> "Response": if config.TRACE: logger.debug(f"{request.url}") try: data = await request.json() except json.JSONDecodeError as e: msg = "add_hosts: invalid json" logger.warning(f"{msg}\n{str(e)}") return util.johann_response(False, msg, 400) logger.debug("add_hosts endpoint called") if "hosts" in data and isinstance(data["hosts"], dict): success, err_msgs, successful_hostnames = _update_hosts( data["hosts"], allow_invalid=False ) if not success: return util.johann_response(False, err_msgs, 400) else: return util.johann_response(True, [], data=successful_hostnames) else: msg = "invalid format for key 'hosts'" logger.warning(msg) return util.johann_response(False, msg, 400)
async def api_get_codehash(request: "Request") -> "Response": if config.TRACE: logger.debug(f"{request.url}") return util.johann_response(True, [], data=util.get_codehash())
async def roll_call(request: "Request") -> "Response": if config.TRACE: logger.debug(f"{request.url}") score_name = request.match_info["score_name"] if score_name not in scores: return util.johann_response(False, f"unrecognized score '{score_name}'", 404) logger.debug(f"{request.method} roll_call for score '{score_name}'") score = scores[score_name] # make sure score isn't already running (e.g. by another user) if score.started_at and not score.finished: return util.johann_response(False, "score already playing", 400) # update score/players if changed if request.method == "POST": data = await request.json() score.create_hosts = data["create_hosts"] # PLANNED: REMOVE score.discard_hosts = data["discard_hosts"] # PLANNED: REMOVE if "players" in data and isinstance(data["players"], dict): posted_players = {} for p_name, p_data in data["players"].items(): try: p: "Player" = PlayerSchema().load(p_data) except marshmallow.ValidationError as e: msg = f"invalid player data provided ({p_name}): {str(e)}" logger.warning(f"roll_call: {msg}") return util.johann_response(False, msg, 400) posted_players[p.name] = p if p.name in score.players: score_player = score.players[p.name] success, err_msg = score_player.copy_from(p, score) if success is None: pass # no changes required elif success: logger.debug( f"roll_call: updated player '{p.name}'" f" to:\n{score_player.dump()}" ) else: msg = f"failed to update player '{p.name}': {err_msg}" logger.warning(f"roll_call: {msg}") return util.johann_response(False, msg, 400) else: msg = f"unrecognized player: '{p.name}'" logger.warning(f"roll_call: {msg}") return util.johann_response(False, msg, 400) else: logger.warning( "roll_call: POST missing or invalid format for key 'players'" ) # actual roll call success, err_msgs = score.validate_create_host_mappings() if not success: logger.warning(f"{score_name}: errors validating host mappings:\n{err_msgs}") return util.johann_response(False, err_msgs, 400) else: score.last_successful_roll_call = datetime.utcnow() return util.johann_response( True, "roll_call successful; you are now free to cue the music" )