diff --git a/hyperglass/cli/commands.py b/hyperglass/cli/commands.py index 02e1d4c..94811b4 100644 --- a/hyperglass/cli/commands.py +++ b/hyperglass/cli/commands.py @@ -36,7 +36,9 @@ def _print_version(ctx, param, value): help=CLI_HELP, context_settings={"help_option_names": ["-h", "--help"], "color": supports_color}, help_headers_color=LABEL, - help_options_custom_colors=random_colors("build-ui", "start", "secret", "setup"), + help_options_custom_colors=random_colors( + "build-ui", "start", "secret", "setup", "system-info" + ), ) @option( "-v", @@ -227,3 +229,17 @@ def setup(unattended): systemd = make_systemd(user) write_to_file(systemd_file, systemd) install_systemd(install_path) + + +@hg.command( + "system-info", + help=cmd_help( + E.THERMOMETER, " Get System Information for a Bug report", supports_color + ), + cls=HelpColorsCommand, +) +def get_system_info(): + """Get CPU, Memory, Disk, Python, & hyperglass version.""" + from hyperglass.cli.util import system_info + + system_info() diff --git a/hyperglass/cli/static.py b/hyperglass/cli/static.py index 0e0b0ab..084e0b8 100644 --- a/hyperglass/cli/static.py +++ b/hyperglass/cli/static.py @@ -45,6 +45,7 @@ class Emoji: LOCK = "\U0001F512 " CLAMP = "\U0001F5DC " BOOKS = "\U0001F4DA " + THERMOMETER = "\U0001F321 " WS = Char(" ") diff --git a/hyperglass/cli/util.py b/hyperglass/cli/util.py index bce2427..f164491 100644 --- a/hyperglass/cli/util.py +++ b/hyperglass/cli/util.py @@ -402,3 +402,57 @@ def install_systemd(app_path): success("Symlinked {s} to {d}", s=service, d=installed) return True + + +def system_info(): + """Create a markdown table of various system information. + + Returns: + {str}: Markdown table + """ + from hyperglass.util.system_info import get_system_info + + values = get_system_info() + + def _code(val): + return f"`{str(val)}`" + + def _bold(val): + return f"**{str(val)}**" + + def _suffix(val, suffix): + return f"{str(val)}{str(suffix)}" + + columns = ( + ("hyperglass Version", _bold), + ("hyperglass Path", _code), + ("Python Version", _code), + ("Platform Info", _code), + ("CPU Info", None), + ("Logical Cores", _code), + ("Physical Cores", _code), + ("Processor Speed", "GHz"), + ("Total Memory", " GB"), + ("Memory Utilization", "%"), + ("Total Disk Space", " GB"), + ("Disk Utilization", "%"), + ) + md_table_lines = ("| Metric | Value |", "| ------ | ----- |") + + for i, metric in enumerate(values): + title, mod = columns[i] + value = metric + + if isinstance(mod, str): + value = _suffix(value, mod) + elif callable(mod): + value = mod(value) + + md_table_lines += (f"| **{title}** | {value} |",) + + md_table = "\n".join(md_table_lines) + + info("Please copy & paste this table in your bug report:\n") + echo(md_table + "\n") + + return True diff --git a/hyperglass/util.py b/hyperglass/util/__init__.py similarity index 100% rename from hyperglass/util.py rename to hyperglass/util/__init__.py diff --git a/hyperglass/util/system_info.py b/hyperglass/util/system_info.py new file mode 100644 index 0000000..82ded69 --- /dev/null +++ b/hyperglass/util/system_info.py @@ -0,0 +1,56 @@ +"""Utility functions for gathering system information.""" + +# Standard Library +import os +import platform + +# Third Party +import psutil as _psutil +from cpuinfo import get_cpu_info as _get_cpu_info + +# Project +from hyperglass.constants import __version__ + + +def _cpu(): + """Construct CPU Information.""" + cpu_info = _get_cpu_info() + brand = cpu_info.get("brand_raw", "") + cores_logical = _psutil.cpu_count() + cores_raw = _psutil.cpu_count(logical=False) + cpu_ghz = _psutil.cpu_freq().current / 1000 + return (brand, cores_logical, cores_raw, cpu_ghz) + + +def _memory(): + """Construct RAM Information.""" + mem_info = _psutil.virtual_memory() + total_gb = round(mem_info.total / 1e9, 2) + usage_percent = mem_info.percent + return (total_gb, usage_percent) + + +def _disk(): + """Construct Disk Information.""" + disk_info = _psutil.disk_usage("/") + total_gb = round(disk_info.total / 1e9, 2) + usage_percent = disk_info.percent + return (total_gb, usage_percent) + + +def get_system_info(): + """Get system info.""" + yield __version__ + + yield os.environ["hyperglass_directory"] + + yield platform.python_version() + + yield platform.platform() + + for c in _cpu(): + yield c + for m in _memory(): + yield m + for d in _disk(): + yield d diff --git a/poetry.lock b/poetry.lock index fac8f9f..7c5ad32 100644 --- a/poetry.lock +++ b/poetry.lock @@ -869,6 +869,25 @@ version = "*" python = "<3.7" version = "*" +[[package]] +category = "main" +description = "Cross-platform lib for process and system monitoring in Python." +name = "psutil" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "5.7.2" + +[package.extras] +test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"] + +[[package]] +category = "main" +description = "Get CPU info with pure Python 2 & 3" +name = "py-cpuinfo" +optional = false +python-versions = "*" +version = "7.0.0" + [[package]] category = "dev" description = "Python style guide checker" @@ -1251,7 +1270,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["jaraco.itertools", "func-timeout"] [metadata] -content-hash = "c7f2eb12a90b07118f86b4e5ef0a0cfc884ea21a077884a9513d31930a50abc3" +content-hash = "358efc064402b70c633e883fa46355aa6021fa65455330cc8d14bcd8be42fb58" python-versions = "^3.6.1" [metadata.files] @@ -1676,6 +1695,22 @@ pre-commit = [ {file = "pre_commit-1.21.0-py2.py3-none-any.whl", hash = "sha256:f92a359477f3252452ae2e8d3029de77aec59415c16ae4189bcfba40b757e029"}, {file = "pre_commit-1.21.0.tar.gz", hash = "sha256:8f48d8637bdae6fa70cc97db9c1dd5aa7c5c8bf71968932a380628c25978b850"}, ] +psutil = [ + {file = "psutil-5.7.2-cp27-none-win32.whl", hash = "sha256:f2018461733b23f308c298653c8903d32aaad7873d25e1d228765e91ae42c3f2"}, + {file = "psutil-5.7.2-cp27-none-win_amd64.whl", hash = "sha256:66c18ca7680a31bf16ee22b1d21b6397869dda8059dbdb57d9f27efa6615f195"}, + {file = "psutil-5.7.2-cp35-cp35m-win32.whl", hash = "sha256:5e9d0f26d4194479a13d5f4b3798260c20cecf9ac9a461e718eb59ea520a360c"}, + {file = "psutil-5.7.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4080869ed93cce662905b029a1770fe89c98787e543fa7347f075ade761b19d6"}, + {file = "psutil-5.7.2-cp36-cp36m-win32.whl", hash = "sha256:d8a82162f23c53b8525cf5f14a355f5d1eea86fa8edde27287dd3a98399e4fdf"}, + {file = "psutil-5.7.2-cp36-cp36m-win_amd64.whl", hash = "sha256:0ee3c36428f160d2d8fce3c583a0353e848abb7de9732c50cf3356dd49ad63f8"}, + {file = "psutil-5.7.2-cp37-cp37m-win32.whl", hash = "sha256:ff1977ba1a5f71f89166d5145c3da1cea89a0fdb044075a12c720ee9123ec818"}, + {file = "psutil-5.7.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a5b120bb3c0c71dfe27551f9da2f3209a8257a178ed6c628a819037a8df487f1"}, + {file = "psutil-5.7.2-cp38-cp38-win32.whl", hash = "sha256:10512b46c95b02842c225f58fa00385c08fa00c68bac7da2d9a58ebe2c517498"}, + {file = "psutil-5.7.2-cp38-cp38-win_amd64.whl", hash = "sha256:68d36986ded5dac7c2dcd42f2682af1db80d4bce3faa126a6145c1637e1b559f"}, + {file = "psutil-5.7.2.tar.gz", hash = "sha256:90990af1c3c67195c44c9a889184f84f5b2320dce3ee3acbd054e3ba0b4a7beb"}, +] +py-cpuinfo = [ + {file = "py-cpuinfo-7.0.0.tar.gz", hash = "sha256:9aa2e49675114959697d25cf57fec41c29b55887bff3bc4809b44ac6f5730097"}, +] pycodestyle = [ {file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"}, {file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"}, diff --git a/pyproject.toml b/pyproject.toml index 9eb2a08..03ad8c8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,6 +38,8 @@ redis = "^3.5.3" uvicorn = "^0.11" uvloop = "^0.14.0" xmltodict = "^0.12.0" +psutil = "^5.7.2" +py-cpuinfo = "^7.0.0" [tool.poetry.dev-dependencies] bandit = "^1.6.2"