#!/usr/bin/python3
#
# Univention Management Console
# module: collecting system information
#
# SPDX-FileCopyrightText: 2011-2025 Univention GmbH
# SPDX-License-Identifier: AGPL-3.0-only
import os
import re
import subprocess
from urllib.parse import urlencode, urlunparse
import requests
import univention.config_registry
import univention.management.console as umc
import univention.management.console.modules as umcm
from univention.management.console.log import MODULE
from univention.management.console.modules import UMC_Error
from univention.management.console.modules.decorators import sanitize, simple_response
from univention.management.console.modules.sanitizers import StringSanitizer
ucr = univention.config_registry.ConfigRegistry()
_ = umc.Translation('univention-management-console-module-sysinfo').translate
[docs]
class Instance(umcm.Base):
def __init__(self):
umcm.Base.__init__(self)
self.mem_regex = re.compile('([0-9]*) kB')
def _call(self, command):
try:
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(stdoutdata, stderrdata) = process.communicate()
return (process.returncode, stdoutdata.decode('UTF-8'), stderrdata.decode('UTF-8'))
except OSError:
return (True, None, None)
[docs]
@simple_response
def get_general_info(self):
DMIDECODE = '/usr/sbin/dmidecode'
MANUFACTURER_CMD = (DMIDECODE, '-s', 'system-manufacturer')
MODEL_CMD = (DMIDECODE, '-s', 'system-product-name')
stdout_list = []
for command in (MANUFACTURER_CMD, MODEL_CMD):
(exitcode, stdout, stderr) = self._call(command)
if exitcode:
MODULE.error('Command %r failed: %s %r %r', command, exitcode, stdout, stderr)
raise UMC_Error(_('Failed to execute command'))
else:
stdout = stdout[:-1] # remove newline character
stdout_list.append(stdout)
result = {}
result['manufacturer'] = stdout_list[0]
result['model'] = stdout_list[1]
return result
[docs]
@sanitize(
manufacturer=StringSanitizer(required=True),
model=StringSanitizer(required=True),
comment=StringSanitizer(required=True),
ticket=StringSanitizer(required=False, default=''),
)
@simple_response
def get_system_info(self, manufacturer, model, comment, ticket=''):
SYSTEM_INFO_CMD = (
'/usr/bin/univention-system-info',
'-m', manufacturer,
'-t', model,
'-c', comment,
'-s', ticket,
'-u')
(exitcode, stdout, _stderr) = self._call(SYSTEM_INFO_CMD)
if exitcode:
MODULE.error('Execution of univention-system-info failed: %s', stdout)
raise UMC_Error('Execution of univention-system-info failed')
result = {}
for line in stdout.splitlines():
try:
info, value = line.split(':', 1)
result[info] = value
except ValueError:
pass
if result.get('mem'):
match = self.mem_regex.match(result['mem'])
if match:
try:
converted_mem = (float(match.groups()[0]) / 1048576)
result['mem'] = '%.2f GB' % converted_mem
result['mem'] = result['mem'].replace('.', ',')
except (IndexError, ValueError):
pass
result.pop('Temp', None) # remove unnecessary entry
return result
[docs]
@simple_response
def get_mail_info(self):
ucr.load()
ADDRESS_VALUE = ucr.get('umc/sysinfo/mail/address', 'feedback@univention.de')
SUBJECT_VALUE = ucr.get('umc/sysinfo/mail/subject', 'Univention System Info')
url = urlunparse(('mailto', '', ADDRESS_VALUE, '', urlencode({'subject': SUBJECT_VALUE}), ''))
result = {}
result['url'] = url.replace('+', '%20')
return result
[docs]
@sanitize(archive=StringSanitizer(required=True))
@simple_response
def upload_archive(self, archive):
ucr.load()
url = ucr.get('umc/sysinfo/upload/url', 'https://forge.univention.org/cgi-bin/system-info-upload.py')
SYSINFO_PATH = '/usr/share/univention-system-info/archives/'
path = os.path.abspath(os.path.join(SYSINFO_PATH, archive))
if not path.startswith(SYSINFO_PATH):
raise UMC_Error('Archive path invalid.')
with open(os.path.join(SYSINFO_PATH, archive), 'rb') as fd:
try:
response = requests.post(url, files={'filename': fd})
response.raise_for_status()
except requests.exceptions.RequestException as exc:
raise UMC_Error('Archive upload failed: %s' % (exc,))
answer = response.text
if answer.startswith('ERROR:'):
raise UMC_Error(answer)
[docs]
@sanitize(traceback=StringSanitizer(), remark=StringSanitizer(), email=StringSanitizer())
@simple_response
def upload_traceback(self, traceback, remark, email):
ucr.load()
ucs_version = '%(version/version)s-%(version/patchlevel)s errata%(version/erratalevel)s' % ucr
if ucr.get('appcenter/apps/ucsschool/version'):
ucs_version = '%s - UCS@school %s' % (ucs_version, ucr['appcenter/apps/ucsschool/version'])
# anonymised id of localhost
uuid_system = ucr.get('uuid/system', '')
url = ucr.get('umc/sysinfo/traceback/url', 'https://forge.univention.org/cgi-bin/system-info-traceback.py')
MODULE.process('Sending %s to %s', traceback, url)
request_data = {
'traceback': traceback,
'remark': remark,
'email': email,
'ucs_version': ucs_version,
'uuid_system': uuid_system,
'uuid_license': ucr.get('uuid/license', ''),
'server_role': ucr.get('server/role'),
}
try:
response = requests.post(url, data=request_data)
response.raise_for_status()
except requests.exceptions.RequestException as exc:
raise UMC_Error('Sending traceback failed: %s' % (exc,))