async def run_query(query): """Execute validated query & parse the results. Arguments: query {object} -- Validated query object Raises: ExecutionError: If stderr exists Returns: {str} -- Parsed output string """ log.debug(f"Query: {query}") parser_map = {"bird": parse_bird_output, "frr": parse_frr_output} parser = parser_map[params.mode] command_raw = operator.attrgetter(".".join( [params.mode, query.afi, query.query_type]))(commands) log.debug(f"Raw Command: {command_raw}") command = command_raw.format(**query.dict()) log.debug(f"Formatted Command: {command}") proc = await asyncio.create_subprocess_shell( command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) stdout, stderr = await proc.communicate() if stderr: err_output = stderr.decode() log.error(err_output) raise ExecutionError(err_output) output = "" if stdout: log.debug(f"Parser: {parser.__name__}") raw_output = stdout.decode() output += await parser(raw=raw_output, query_data=query, not_found=params.not_found_message) return output if not output and proc.returncode == 0: output = await parser(raw="", query_data=query, not_found=params.not_found_message) return output
async def parse_frr_output(raw, query_data, not_found): """Parse raw CLI output from FRR (vtysh) and return parsed output. Arguments: raw {str} -- Raw output from vtysh query_data {object} -- Validated query object not_found {str} -- Lookup not found message template Returns: {str} -- Parsed output """ raw_split = raw.strip() if not raw_split: notfound_message = not_found.format( target=query_data.target, afi=AFI_DISPLAY_MAP[query_data.afi]) output = notfound_message else: output = raw_split log.debug(f"Parsed output:\n{output}") return output
async def query_entrypoint(query: EncodedRequest): """Validate and process input request. Arguments: query {dict} -- Encoded JWT Returns: {obj} -- JSON response """ try: log.debug(f"Raw Query JSON: {query.json()}") decrypted_query = await jwt_decode(query.encoded) decrypted_query = json.loads(decrypted_query) log.debug(f"Decrypted Query: {decrypted_query}") validated_query = Request(**decrypted_query) query_output = await run_query(validated_query) log.debug(f"Query Output:\n{query_output}") encoded = await jwt_encode(query_output) return {"encoded": encoded} except ValidationError as err_validation: raise RequestValidationError(str(err_validation)) except HyperglassAgentError as err_agent: raise HTTPException(status_code=err_agent.code, detail=str(err_agent))
async def get_bird_version(): """Get BIRD version from command line. Raises: ExecutionError: Raised when `birdc` is not found on the system. ExecutionError: Raised when the output is unreadable or contains errors. Returns: {int} -- Major BIRD version. """ proc = await asyncio.create_subprocess_shell( cmd="bird --version", stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) stdout, stderr = await proc.communicate() if stdout: raw_output = stdout.decode("utf-8") if stderr and b"BIRD version" in stderr: raw_output = stderr.decode("utf-8") elif stderr and b"command not found" in stderr: raise ExecutionError( ("BIRD mode is configured, but bird does not appear to be " f'installed: {stderr.decode("utf-8")}')) elif stderr and b"BIRD version" not in stderr: raise ExecutionError(stderr.decode("utf-8")) # Extract numbers from string as list of numbers version_str = re.findall(r"\d+", raw_output) # Filter major release number & convert to int version = int(version_str[0]) log.debug(f"BIRD Major Version: {version_str[0]}") return version
async def parse_bird_output(raw, query_data, not_found): """Parse raw BIRD output and return parsed output. Arguments: raw {str} -- Raw BIRD output query_data {object} -- Validated query object not_found {str} -- Lookup not found message template Returns: str -- Parsed output """ raw_split = re.split(r"(Table)", raw.strip()) raw_joined = "".join(raw_split[1::]) if not raw_joined: notfound_message = not_found.format( target=query_data.target, afi=AFI_DISPLAY_MAP[query_data.afi]) output = notfound_message else: output = raw_joined log.debug(f"Parsed output:\n{output}") return output
_commands = _raw_config.pop("commands", None) _user_config = General(**_raw_config) if _commands is not None: _user_commands = Commands.import_params(mode=_user_config.mode, **_commands) else: _user_commands = Commands.import_params(mode=_user_config.mode) except ValidationError as validation_errors: _errors = validation_errors.errors() for error in _errors: raise ConfigInvalid( field=": ".join([str(item) for item in error["loc"]]), error_msg=error["msg"], ) LOG_LEVEL = "INFO" if _user_config.debug: LOG_LEVEL = "DEBUG" LOG_HANDLER["level"] = LOG_LEVEL log.remove() log.configure(handlers=[LOG_HANDLER], levels=LOG_LEVELS) log.debug("Debugging Enabled") params = _user_config commands = _user_commands log.debug(params.json()) log.debug(commands.json())