]> git.openstreetmap.org Git - nominatim.git/commitdiff
Merge remote-tracking branch 'upstream/master' into collect_os_info.sh
authorTareq Al-Ahdal <tareqoalahdal@gmail.com>
Fri, 12 Aug 2022 21:53:39 +0000 (05:53 +0800)
committerTareq Al-Ahdal <tareqoalahdal@gmail.com>
Fri, 12 Aug 2022 21:53:39 +0000 (05:53 +0800)
utils/collect_os_info.py [new file with mode: 0644]
utils/collect_os_info.sh [new file with mode: 0755]

diff --git a/utils/collect_os_info.py b/utils/collect_os_info.py
new file mode 100644 (file)
index 0000000..0f5bf04
--- /dev/null
@@ -0,0 +1,158 @@
+
+import os
+from pathlib import Path
+import subprocess
+import sys
+from typing import Optional, Union
+
+# external requirement
+import psutil
+
+# from nominatim.version import NOMINATIM_VERSION
+# from nominatim.db.connection import connect
+
+
+class ReportSystemInformation:
+       """Generate a report about the host system including software versions, memory,
+          storage, and database configuration."""
+       def __init__(self):
+               self._memory: int = psutil.virtual_memory().total
+               self.friendly_memory: str = self._friendly_memory_string(self._memory)
+               # psutil.cpu_count(logical=False) returns the number of CPU cores.
+               # For number of logical cores (Hypthreaded), call psutil.cpu_count() or os.cpu_count() 
+               self.num_cpus: int = psutil.cpu_count(logical=False)
+               self.os_info: str = self._os_name_info()
+
+### These are commented out because they have not been tested.
+#              self.nominatim_ver: str = '{0[0]}.{0[1]}.{0[2]}-{0[3]}'.format(NOMINATIM_VERSION)
+#      self._pg_version = conn.server_version_tuple()
+#      self._postgis_version = conn.postgis_version_tuple()
+#              self.postgresql_ver: str = self._convert_version(self._pg_version)
+#              self.postgis_ver: str = self._convert_version(self._postgis_version)
+
+               self.nominatim_ver: str = ""
+               self.postgresql_ver: str = ""
+               self.postgresql_config: str = ""
+               self.postgis_ver: str = ""
+
+               # the below commands require calling the shell to gather information
+               self.disk_free: str = self._run_command(["df", "-h"])
+               self.lsblk: str = self._run_command("lsblk")
+               # psutil.disk_partitions() <- this function is similar to the above, but it is cross platform
+
+               # Note: `systemd-detect-virt` command only works on Linux, on other OSes
+               # should give a message: "Unknown (unable to find the 'systemd-detect-virt' command)"
+               self.container_vm_env: str = self._run_command("systemd-detect-virt")
+
+       def _convert_version(self, ver_tup: tuple) -> str:
+               """converts tuple version (ver_tup) to a string representation"""
+               return ".".join(map(str,ver_tup))
+
+       def _friendly_memory_string(self, mem: int) -> str:
+               """Create a user friendly string for the amount of memory specified as mem"""
+               mem_magnitude = ('bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB')
+               mag = 0
+               # determine order of magnitude
+               while mem > 1000:
+                       mem /= 1000
+                       mag += 1
+               
+               return f"{mem:.1f} {mem_magnitude[mag]}"
+
+
+       def _run_command(self, cmd: Union[str, list]) -> str:
+               """Runs a command using the shell and returns the output from stdout"""
+               try:
+                       if sys.version_info < (3, 7):
+                               cap_out = subprocess.run(cmd, stdout=subprocess.PIPE)
+                       else:
+                               cap_out = subprocess.run(cmd, capture_output=True)
+                       return cap_out.stdout.decode("utf-8")
+               except FileNotFoundError:
+                               # non-Linux system should end up here
+                               return f"Unknown (unable to find the '{cmd}' command)"
+
+
+       def _os_name_info(self) -> str:
+               """Obtain Operating System Name (and possibly the version)"""
+
+               os_info = None
+               # man page os-release(5) details meaning of the fields
+               if Path("/etc/os-release").is_file():
+                       os_info = self._from_file_find_line_portion("/etc/os-release", "PRETTY_NAME", "=")
+               # alternative location 
+               elif Path("/usr/lib/os-release").is_file():
+                       os_info = self._from_file_find_line_portion("/usr/lib/os-release", "PRETTY_NAME", "=")
+
+               # fallback on Python's os name
+               if(os_info is None or os_info == ""):
+                       os_info = os.name
+
+               # if the above is insufficient, take a look at neofetch's approach to OS detection              
+               return os_info
+
+
+       # Note: Intended to be used on informational files like /proc
+       def _from_file_find_line_portion(self, filename: str, start: str, sep: str,
+                                                                        fieldnum: int = 1) -> Optional[str]:
+               """open filename, finds the line starting with the 'start' string.
+                  Splits the line using seperator and returns a "fieldnum" from the split."""
+               with open(filename) as fh:
+                       for line in fh:
+                               if line.startswith(start):
+                                       result = line.split(sep)[fieldnum].strip()
+                                       return result
+
+       def report(self, out = sys.stdout, err = sys.stderr) -> None:
+               """Generates the Markdown report. 
+               
+               Optionally pass out or err parameters to redirect the output of stdout
+                and stderr to other file objects."""
+               
+               # NOTE: This should be a report format.  Any conversions or lookup has be
+               #  done, do that action in the __init__() or another function. 
+               message = """
+Use this information in your issue report at https://github.com/osm-search/Nominatim/issues
+Copy and paste or redirect the output of the file:
+    $ ./collect_os_info.py > report.md
+"""
+               report = f"""
+**Software Environment:**
+- Python version: {sys.version}
+- Nominatim version: {self.nominatim_ver} 
+- PostgreSQL version: {self.postgresql_ver} 
+- PostGIS version: {self.postgis_ver}
+- OS: {self.os_info}
+
+
+**Hardware Configuration:**
+- RAM: {self.friendly_memory}
+- number of CPUs: {self.num_cpus}
+- bare metal/AWS/other cloud service (per systemd-detect-virt(1)): {self.container_vm_env} 
+- type and size of disks:
+**`df -h` - df - report file system disk space usage: **
+```
+{self.disk_free}
+```
+
+**lsblk - list block devices: **
+```
+{self.lsblk}
+```
+
+
+**Postgresql Configuration:**
+```
+{self.postgresql_config}
+```
+**Notes**
+Please add any notes about anything above anything above that is incorrect.
+       """
+
+               print(message, file = err)
+               print(report, file = out)
+
+
+if __name__ == "__main__":
+       sys_info = ReportSystemInformation()
+       sys_info.report()
\ No newline at end of file
diff --git a/utils/collect_os_info.sh b/utils/collect_os_info.sh
new file mode 100755 (executable)
index 0000000..15ea6c3
--- /dev/null
@@ -0,0 +1,94 @@
+#!/usr/bin/env bash
+
+Description="The purpose of this script is to collect system information for bug reports.\n
+Submit issues to https://github.com/osm-search/Nominatim/issues"
+
+
+####### Gather the Information ##################################################
+# Separate the information gathering from the report generation.  Dividing these
+# makes it easier to make trivial changes by not have to learn the other portion
+# of this script.
+
+# Nominatium version
+# NOTE: Getting this version will NOT work if it is being ran from in another
+# folder than Nominatim/utils.  It call python3 to import version.py locally and
+# prints it in the version format. 
+NominatimVersion=`cd ../nominatim/ && python3 -c "import version; print('{0[0]}.{0[1]}.{0[2]}-{0[3]}'.format(version.NOMINATIM_VERSION))"`
+
+# PostgreSQL version
+PostgreSQLVersion=`postgres --version`
+if [ "$?" -ne "0" ]
+then
+  PostgreSQLVersion="Not installed"
+fi
+
+# - PostGIS version:
+# The command for this should look something like this:
+#       psql -U nominatim -d mydatabase -c 'SELECT PostGIS_full_version();'
+# ASSUME the username is nominatim
+# This needs to be ran under the account with the appropriate permissions.
+# This has been left blank.
+PostGISVersion=
+
+# There are different ways to getting the Linux OS information.
+# https://www.cyberciti.biz/faq/how-to-check-os-version-in-linux-command-line/
+# /etc/os-release has a number of representations of the OS
+# PRETTY_NAME is pity.
+OperatingSystem=`grep '^PRETTY_NAME' /etc/os-release | cut -d'=' -f2`
+
+RAM=`grep ^MemTotal /proc/meminfo | cut -d':' -f2`
+
+# In /proc/cupinfo: siblings seems to refer to total cores like hyperthreaded cores.
+# The hyperthreaded cores could be included if that is needed.
+NumCPUs=`grep '^cpu cores' /proc/cpuinfo | head -1 | cut -d':' -f2`
+
+
+# - type and size of disks:
+# could use `sudo fdisk -l` or `mount` to print this, but snaps have made this
+# worse than useless with loop devices on Ubuntu.  
+# `df -h` - show the free space on drives
+# `lsblk` - this tell you what the server has not necessarily this machine.  So in a container environment
+#  (like docker) this wouldn't be the correct report.
+# This guide shows ways to get various storage device information: https://www.cyberciti.biz/faq/find-hard-disk-hardware-specs-on-linux/
+
+# - bare metal/AWS/other cloud service:
+# Unsure of how to detect this, but it might be useful for reporting disk storage.
+# One options would be to prompt the user something like this:
+# Enter system configuration (1) bare metal (2) AWS (3) Other Cloud (4) Docker (5) Other: _
+
+# ------ What do these commands do? -------------------------------------------
+# "cut -d':' -f2"      command take the line and splits it at the semicolon(:)
+#                      and returns the portion in the second (2nd) "field"
+#
+# "head -1"            returns the first line that matches
+#
+
+####### Print the Markdown Report ######################################################
+# 1>&2 redirects echo to print to stderr instead of stdout
+
+echo 1>&2
+echo -e $Description 1>&2
+echo Copy and paste or redirect the output of the file:  1>&2
+echo "     \$ ./collect_os_info.sh > report.md" 1>&2
+echo 1>&2
+
+
+echo "**Software Environment (please complete the following information):**"
+echo - Nominatim version: $NominatimVersion
+echo - PostgreSQL version: $PostgreSQLVersion 
+echo - PostGIS version: $PostGISVersion 
+echo - OS: $OperatingSystem
+echo
+echo
+
+
+echo "**Hardware Configuration (please correct the following information):**"
+echo - RAM: $RAM
+echo - number of CPUs: $NumCPUs
+echo - type and size of disks:
+echo - bare metal/AWS/other cloud service: 
+echo
+echo
+echo **Postgresql Configuration:**
+echo
+