unrhodecode/rhodecode/apps/admin/views/system_info.py
2026-01-25 13:57:44 +01:00

278 lines
12 KiB
Python

# Copyright (C) 2016-2024 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
import logging
import urllib.request
import urllib.error
import urllib.parse
import os
from pyramid.httpexceptions import HTTPFound
import rhodecode
from rhodecode.apps._base import BaseAppView
from rhodecode.apps._base.navigation import navigation_list
from rhodecode.apps.admin.update_service import RcStackUpdateService
from rhodecode.lib import helpers as h
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
from rhodecode.lib.utils2 import str2bool
from rhodecode.lib import system_info
from rhodecode.model.update import UpdateModel
log = logging.getLogger(__name__)
class AdminSystemInfoSettingsView(BaseAppView):
def load_default_context(self):
c = self._get_local_tmpl_context()
return c
def get_env_data(self):
black_list = [
"NIX_LDFLAGS",
"NIX_CFLAGS_COMPILE",
"propagatedBuildInputs",
"propagatedNativeBuildInputs",
"postInstall",
"buildInputs",
"buildPhase",
"preShellHook",
"preShellHook",
"preCheck",
"preBuild",
"postShellHook",
"postFixup",
"postCheck",
"nativeBuildInputs",
"installPhase",
"installCheckPhase",
"checkPhase",
"configurePhase",
"shellHook",
]
secret_list = ["RHODECODE_USER_PASS"]
for k, v in sorted(os.environ.items()):
if k in black_list:
continue
if k in secret_list:
v = "*****"
yield k, v
@LoginRequired()
@HasPermissionAllDecorator("hg.admin")
def settings_system_info(self):
_ = self.request.translate
c = self.load_default_context()
c.active = "system"
c.navlist = navigation_list(self.request)
# TODO(marcink), figure out how to allow only selected users to do this
c.allowed_to_snapshot = self._rhodecode_user.admin
snapshot = str2bool(self.request.params.get("snapshot"))
c.rhodecode_update_url = UpdateModel().get_update_url()
c.env_data = self.get_env_data()
server_info = system_info.get_system_info(self.request.environ)
for key, val in server_info.items():
setattr(c, key, val)
def val(name, subkey="human_value"):
return server_info[name][subkey]
def state(name):
return server_info[name]["state"]
def val2(name):
val = server_info[name]["human_value"]
state = server_info[name]["state"]
return val, state
update_info_msg = _(
"Note: please make sure this server can access `${url}` for the update link to work",
mapping=dict(url=c.rhodecode_update_url),
)
version = UpdateModel().get_stored_version()
is_outdated = UpdateModel().is_outdated(rhodecode.__version__, version)
update_state = (
{"type": "warning", "message": "New version available: {}".format(version)} if is_outdated else {}
)
update_service = self._get_rc_update_service()
update_feature_available = update_service.feature_available()
update_literal = ""
if update_feature_available:
update_service_state = update_service.get_state()
if update_service_state == RcStackUpdateService.State.READY:
update_literal = '<br/> <span class="link" id="rcstack_update" >%s.</span>' % (_("Update RcStack"))
else:
update_literal = "<br/> <span>%s.</span>" % (
_("Update in progress, please wait. You can check logs on the host machine.")
)
c.data_items = [
# update info
(
_("Update info"),
h.literal(
'<span class="link" id="check_for_update" >%s.</span>' % (_("Check for updates"))
+ "<br/> <span >%s.</span>" % (update_info_msg)
+ update_literal
),
"",
),
# RhodeCode specific
(_("RhodeCode Version"), val("rhodecode_app")["text"], state("rhodecode_app")),
(_("Latest version"), version, update_state),
(_("RhodeCode Base URL"), val("rhodecode_config")["config"].get("app.base_url"), state("rhodecode_config")),
(_("RhodeCode Server IP"), val("server")["server_ip"], state("server")),
(_("RhodeCode Server ID"), val("server")["server_id"], state("server")),
(_("RhodeCode Configuration"), val("rhodecode_config")["path"], state("rhodecode_config")),
(_("RhodeCode Certificate"), val("rhodecode_config")["cert_path"], state("rhodecode_config")),
(
_("Workers"),
val("rhodecode_config")["config"]["server:main"].get("workers", "?"),
state("rhodecode_config"),
),
(
_("Worker Type"),
val("rhodecode_config")["config"]["server:main"].get("worker_class", "sync"),
state("rhodecode_config"),
),
("", "", ""), # spacer
# Database
(_("Database"), val("database")["url"], state("database")),
(_("Database version"), val("database")["version"], state("database")),
("", "", ""), # spacer
# Platform/Python
(_("Platform"), val("platform")["name"], state("platform")),
(_("Platform UUID"), val("platform")["uuid"], state("platform")),
(_("Lang"), val("locale"), state("locale")),
(_("Python version"), val("python")["version"], state("python")),
(_("Python path"), val("python")["executable"], state("python")),
("", "", ""), # spacer
# Systems stats
(_("CPU"), val("cpu")["text"], state("cpu")),
(_("Load"), val("load")["text"], state("load")),
(_("Memory"), val("memory")["text"], state("memory")),
(_("Uptime"), val("uptime")["text"], state("uptime")),
("", "", ""), # spacer
# ulimit
(_("Ulimit"), val("ulimit")["text"], state("ulimit")),
# Repo storage
(_("Storage location"), val("storage")["path"], state("storage")),
(_("Storage info"), val("storage")["text"], state("storage")),
(_("Storage inodes"), val("storage_inodes")["text"], state("storage_inodes")),
("", "", ""), # spacer
(_("Gist storage location"), val("storage_gist")["path"], state("storage_gist")),
(_("Gist storage info"), val("storage_gist")["text"], state("storage_gist")),
("", "", ""), # spacer
(_("Artifacts storage backend"), val("storage_artifacts")["type"], state("storage_artifacts")),
(_("Artifacts storage location"), val("storage_artifacts")["path"], state("storage_artifacts")),
(_("Artifacts info"), val("storage_artifacts")["text"], state("storage_artifacts")),
("", "", ""), # spacer
(_("Archive cache storage backend"), val("storage_archive")["type"], state("storage_archive")),
(_("Archive cache storage location"), val("storage_archive")["path"], state("storage_archive")),
(_("Archive cache info"), val("storage_archive")["text"], state("storage_archive")),
("", "", ""), # spacer
(_("Temp storage location"), val("storage_temp")["path"], state("storage_temp")),
(_("Temp storage info"), val("storage_temp")["text"], state("storage_temp")),
("", "", ""), # spacer
(_("Search info"), val("search")["text"], state("search")),
(_("Search location"), val("search")["location"], state("search")),
("", "", ""), # spacer
# VCS specific
(_("VCS Backends"), val("vcs_backends"), state("vcs_backends")),
(_("VCS Server"), val("vcs_server")["text"], state("vcs_server")),
(_("GIT"), val("git"), state("git")),
(_("HG"), val("hg"), state("hg")),
(_("SVN"), val("svn"), state("svn")),
]
c.rhodecode_data_items = [
(k, v) for k, v in sorted((val("rhodecode_server_config") or {}).items(), key=lambda x: x[0].lower())
]
c.vcsserver_data_items = [
(k, v) for k, v in sorted((val("vcs_server_config") or {}).items(), key=lambda x: x[0].lower())
]
if snapshot:
if c.allowed_to_snapshot:
c.data_items.pop(0) # remove server info
self.request.override_renderer = "admin/settings/settings_system_snapshot.mako"
else:
h.flash("You are not allowed to do this", category="warning")
return self._get_template_context(c)
@LoginRequired()
@HasPermissionAllDecorator("hg.admin")
def settings_system_info_rcstack_update(self):
update_service = self._get_rc_update_service()
update_feature_available = update_service.feature_available()
if update_feature_available:
response = update_service.rcstack_update()
log.debug(f"update server response: {response}")
return HTTPFound(h.route_path("admin_settings_system"))
def _get_rc_update_service(self) -> RcStackUpdateService:
host = os.environ.get("RC_UPDATE_HOST", None)
port = os.environ.get("RC_UPDATE_PORT", 10025)
return RcStackUpdateService(server_host=host, server_port=port)
@LoginRequired()
@HasPermissionAllDecorator("hg.admin")
def settings_system_info_check_update(self):
_ = self.request.translate
c = self.load_default_context()
update_url = UpdateModel().get_update_url()
def _err(s):
return f'<div style="color:#ff8888; padding:4px 0px">{s}</div>'
try:
data = UpdateModel().get_update_data(update_url)
except urllib.error.URLError as e:
log.exception("Exception contacting upgrade server")
self.request.override_renderer = "string"
return _err("Failed to contact upgrade server: %r" % e)
except ValueError as e:
log.exception("Bad data sent from update server")
self.request.override_renderer = "string"
return _err("Bad data sent from update server")
latest = data["versions"][0]
c.update_url = update_url
c.latest_data = latest
c.latest_ver = (latest["version"] or "").strip()
c.cur_ver = self.request.GET.get("ver") or rhodecode.__version__
c.should_upgrade = False
is_outdated = UpdateModel().is_outdated(c.cur_ver, c.latest_ver)
if is_outdated:
c.should_upgrade = True
c.important_notices = latest["general"]
UpdateModel().store_version(latest["version"])
return self._get_template_context(c)