def diagnosis_show(categories=[], issues=False, full=False, share=False, human_readable=False): if not os.path.exists(DIAGNOSIS_CACHE): logger.warning(m18n.n("diagnosis_never_ran_yet")) return # Get all the categories all_categories = _list_diagnosis_categories() all_categories_names = [category for category, _ in all_categories] # Check the requested category makes sense if categories == []: categories = all_categories_names else: unknown_categories = [ c for c in categories if c not in all_categories_names ] if unknown_categories: raise YunohostValidationError( "diagnosis_unknown_categories", categories=", ".join(unknown_categories)) # Fetch all reports all_reports = [] for category in categories: try: report = Diagnoser.get_cached_report(category) except Exception as e: logger.error( m18n.n("diagnosis_failed", category=category, error=str(e))) continue Diagnoser.i18n(report, force_remove_html_tags=share or human_readable) add_ignore_flag_to_issues(report) if not full: del report["timestamp"] del report["cached_for"] report["items"] = [ item for item in report["items"] if not item["ignored"] ] for item in report["items"]: del item["meta"] del item["ignored"] if "data" in item: del item["data"] if issues: report["items"] = [ item for item in report["items"] if item["status"] in ["WARNING", "ERROR"] ] # Ignore this category if no issue was found if not report["items"]: continue all_reports.append(report) if share: from yunohost.utils.yunopaste import yunopaste content = _dump_human_readable_reports(all_reports) url = yunopaste(content) logger.info(m18n.n("log_available_on_yunopaste", url=url)) if msettings.get("interface") == "api": return {"url": url} else: return elif human_readable: print(_dump_human_readable_reports(all_reports)) else: return {"reports": all_reports}
def log_show(path, number=None, share=False, filter_irrelevant=False, with_suboperations=False): """ Display a log file enriched with metadata if any. If the file_name is not an absolute path, it will try to search the file in the unit operations log path (see OPERATIONS_PATH). Argument: file_name number share """ if share: filter_irrelevant = True if filter_irrelevant: filters = [ r"set [+-]x$", r"set [+-]o xtrace$", r"local \w+$", r"local legacy_args=.*$", r".*Helper used in legacy mode.*", r"args_array=.*$", r"local -A args_array$", r"ynh_handle_getopts_args", r"ynh_script_progression", ] else: filters = [] def _filter_lines(lines, filters=[]): filters = [re.compile(f) for f in filters] return [ line for line in lines if not any(f.search(line.strip()) for f in filters) ] # Normalize log/metadata paths and filenames abs_path = path log_path = None if not path.startswith("/"): abs_path = os.path.join(OPERATIONS_PATH, path) if os.path.exists(abs_path) and not path.endswith(METADATA_FILE_EXT): log_path = abs_path if abs_path.endswith(METADATA_FILE_EXT) or abs_path.endswith(LOG_FILE_EXT): base_path = "".join(os.path.splitext(abs_path)[:-1]) else: base_path = abs_path base_filename = os.path.basename(base_path) md_path = base_path + METADATA_FILE_EXT if log_path is None: log_path = base_path + LOG_FILE_EXT if not os.path.exists(md_path) and not os.path.exists(log_path): raise YunohostValidationError("log_does_exists", log=path) infos = {} # If it's a unit operation, display the name and the description if base_path.startswith(CATEGORIES_PATH): infos["description"] = _get_description_from_name(base_filename) infos["name"] = base_filename if share: from yunohost.utils.yunopaste import yunopaste content = "" if os.path.exists(md_path): content += read_file(md_path) content += "\n============\n\n" if os.path.exists(log_path): actual_log = read_file(log_path) content += "\n".join(_filter_lines(actual_log.split("\n"), filters)) url = yunopaste(content) logger.info(m18n.n("log_available_on_yunopaste", url=url)) if msettings.get("interface") == "api": return {"url": url} else: return # Display metadata if exist if os.path.exists(md_path): try: metadata = read_yaml(md_path) except MoulinetteError as e: error = m18n.n("log_corrupted_md_file", md_file=md_path, error=e) if os.path.exists(log_path): logger.warning(error) else: raise YunohostError(error) else: infos["metadata_path"] = md_path infos["metadata"] = metadata if "log_path" in metadata: log_path = metadata["log_path"] if with_suboperations: def suboperations(): try: log_start = _get_datetime_from_name(base_filename) except ValueError: return for filename in os.listdir(OPERATIONS_PATH): if not filename.endswith(METADATA_FILE_EXT): continue # We first retrict search to a ~48h time window to limit the number # of .yml we look into try: date = _get_datetime_from_name(base_filename) except ValueError: continue if (date < log_start) or ( date > log_start + timedelta(hours=48)): continue try: submetadata = read_yaml( os.path.join(OPERATIONS_PATH, filename)) except Exception: continue if submetadata and submetadata.get( "parent") == base_filename: yield { "name": filename[:-len(METADATA_FILE_EXT)], "description": _get_description_from_name( filename[:-len(METADATA_FILE_EXT)]), "success": submetadata.get("success", "?"), } metadata["suboperations"] = list(suboperations()) # Display logs if exist if os.path.exists(log_path): from yunohost.service import _tail if number and filters: logs = _tail(log_path, int(number * 4)) elif number: logs = _tail(log_path, int(number)) else: logs = read_file(log_path) logs = _filter_lines(logs, filters) if number: logs = logs[-number:] infos["log_path"] = log_path infos["logs"] = logs return infos