mirror of
https://github.com/akuker/RASCSI.git
synced 2025-04-06 22:37:17 +00:00
Get and set pretty hostname to use as system name in Web UI (#997)
- Display the pretty hostname as system name in header - Move IP and hostname down to the footer - New endpoint for setting the pretty hostname, plus form field in the Web UI - (unrelated) Use platform.uname() instead of shell uname - (unrelated) Better logic for fetching the Mac HD Drivers zip file in easyinstall.sh
This commit is contained in:
parent
2645656199
commit
0d95890887
@ -19,7 +19,10 @@ RUN apt-get update \
|
||||
RUN groupadd pi
|
||||
RUN useradd --create-home --shell /bin/bash -g pi pi
|
||||
RUN echo "pi ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
|
||||
RUN echo "pi:rascsi" | chpasswd
|
||||
RUN echo "pi:rascsi" | chpasswd
|
||||
|
||||
# Allows custom PATH for mock commands to work when executing with sudo
|
||||
RUN sed -i 's/^Defaults\tsecure_path/#Defaults\tsecure_path./' /etc/sudoers
|
||||
|
||||
RUN mkdir /home/pi/shared_files
|
||||
RUN touch /etc/dhcpcd.conf
|
||||
|
@ -11,6 +11,9 @@ fi
|
||||
# Start Nginx service
|
||||
nginx
|
||||
|
||||
# Use mock commands
|
||||
export PATH="/home/pi/RASCSI/python/web/mock/bin:$PATH"
|
||||
|
||||
# Pass args to web UI start script
|
||||
if [[ $RASCSI_PASSWORD ]]; then
|
||||
/home/pi/RASCSI/python/web/start.sh "$@" --password=$RASCSI_PASSWORD
|
||||
|
@ -626,11 +626,13 @@ function installHfdisk() {
|
||||
|
||||
# Fetch HFS drivers that the Web Interface uses
|
||||
function fetchHardDiskDrivers() {
|
||||
if [ ! -d "$BASE/mac-hard-disk-drivers" ]; then
|
||||
DRIVER_ARCHIVE="mac-hard-disk-drivers"
|
||||
if [ ! -d "$BASE/$DRIVER_ARCHIVE" ]; then
|
||||
cd "$BASE" || exit 1
|
||||
wget -r https://www.dropbox.com/s/gcs4v5pcmk7rxtb/mac-hard-disk-drivers.zip?dl=0
|
||||
unzip -d mac-hard-disk-drivers mac-hard-disk-drivers.zip
|
||||
rm mac-hard-disk-drivers.zip
|
||||
# -N option overwrites if downloaded file is newer than existing file
|
||||
wget -N "https://www.dropbox.com/s/gcs4v5pcmk7rxtb/$DRIVER_ARCHIVE.zip?dl=1" -O "$DRIVER_ARCHIVE.zip"
|
||||
unzip -d "$DRIVER_ARCHIVE" "$DRIVER_ARCHIVE.zip"
|
||||
rm "$DRIVER_ARCHIVE.zip"
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -3,11 +3,12 @@ Module with methods that interact with the Pi system
|
||||
"""
|
||||
import subprocess
|
||||
import logging
|
||||
from subprocess import run
|
||||
from subprocess import run, CalledProcessError
|
||||
from shutil import disk_usage
|
||||
from re import findall, match
|
||||
from socket import socket, gethostname, AF_INET, SOCK_DGRAM
|
||||
from pathlib import Path
|
||||
from platform import uname
|
||||
|
||||
from rascsi.common_settings import SHELL_ERROR
|
||||
|
||||
@ -37,20 +38,6 @@ class SysCmds:
|
||||
logging.warning(SHELL_ERROR, error.cmd, error.stderr.decode("utf-8"))
|
||||
ra_git_version = ""
|
||||
|
||||
try:
|
||||
os_version = (
|
||||
subprocess.run(
|
||||
["uname", "--kernel-name", "--kernel-release", "--machine"],
|
||||
capture_output=True,
|
||||
check=True,
|
||||
)
|
||||
.stdout.decode("utf-8")
|
||||
.strip()
|
||||
)
|
||||
except subprocess.CalledProcessError as error:
|
||||
logging.warning(SHELL_ERROR, " ".join(error.cmd), error.stderr.decode("utf-8"))
|
||||
os_version = "Unknown OS"
|
||||
|
||||
PROC_MODEL_PATH = "/proc/device-tree/model"
|
||||
SYS_VENDOR_PATH = "/sys/devices/virtual/dmi/id/sys_vendor"
|
||||
SYS_PROD_PATH = "/sys/devices/virtual/dmi/id/product_name"
|
||||
@ -77,7 +64,11 @@ class SysCmds:
|
||||
else:
|
||||
hardware = "Unknown Device"
|
||||
|
||||
return {"git": ra_git_version, "env": f"{hardware}, {os_version}" }
|
||||
env = uname()
|
||||
return {
|
||||
"git": ra_git_version,
|
||||
"env": f"{hardware}, {env.system} {env.release} {env.machine}",
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def running_proc(daemon):
|
||||
@ -172,6 +163,42 @@ class SysCmds:
|
||||
sock.close()
|
||||
return ip_addr, host
|
||||
|
||||
@staticmethod
|
||||
def get_pretty_host():
|
||||
"""
|
||||
Returns either the pretty hostname if set, or the regular hostname as fallback.
|
||||
"""
|
||||
try:
|
||||
process = run(
|
||||
["hostnamectl", "status", "--pretty"],
|
||||
capture_output=True,
|
||||
check=True,
|
||||
)
|
||||
pretty_hostname = process.stdout.decode("utf-8").rstrip()
|
||||
if pretty_hostname:
|
||||
return pretty_hostname
|
||||
except CalledProcessError as error:
|
||||
logging.error(str(error))
|
||||
|
||||
return gethostname()
|
||||
|
||||
@staticmethod
|
||||
def set_pretty_host(name):
|
||||
"""
|
||||
Set the pretty hostname for the system
|
||||
"""
|
||||
try:
|
||||
process = run(
|
||||
["sudo", "hostnamectl", "set-hostname", "--pretty", name],
|
||||
capture_output=False,
|
||||
check=True,
|
||||
)
|
||||
except CalledProcessError as error:
|
||||
logging.error(str(error))
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def get_logs(lines, scope):
|
||||
"""
|
||||
|
21
python/web/mock/bin/hostnamectl
Executable file
21
python/web/mock/bin/hostnamectl
Executable file
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
TMP_FILE="/tmp/hostnamectl_pretty.tmp"
|
||||
|
||||
if [[ "$1" == "set-hostname" && "$2" == "--pretty" ]]; then
|
||||
if [[ -z "$3" ]]; then
|
||||
rm "$TMP_FILE" 2>/dev/null || true
|
||||
else
|
||||
echo "$3" > $TMP_FILE
|
||||
fi
|
||||
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$1" == "status" ]]; then
|
||||
cat "$TMP_FILE" 2>/dev/null
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Mock does not recognize: $0 $@"
|
||||
exit 1
|
@ -208,12 +208,12 @@ select {
|
||||
*/
|
||||
div.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
div.header div.title {
|
||||
order: 1;
|
||||
text-align: left;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
div.header div.title h1 {
|
||||
@ -227,7 +227,18 @@ div.header div.title a {
|
||||
}
|
||||
|
||||
div.header div.hostname {
|
||||
display: none;
|
||||
color: #ccc;
|
||||
padding: 0 0.5rem;
|
||||
order: 2;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
div.header div.hostname span {
|
||||
display: inline-block;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 1rem;
|
||||
padding: 0.125rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
div.header div.login-status {
|
||||
@ -253,9 +264,10 @@ div.header div.authentication-disabled {
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
|
||||
@media (max-width: 820px) {
|
||||
@media (max-width: 900px) {
|
||||
div.header {
|
||||
min-height: 3.5rem; /* Safari 14 iOS and iPad OS */
|
||||
background: var(--dark);
|
||||
}
|
||||
|
||||
body:not(.logged-in) div.header {
|
||||
@ -263,10 +275,6 @@ div.header div.authentication-disabled {
|
||||
min-height: 8.875rem; /* Safari 14 iOS and iPad OS */
|
||||
}
|
||||
|
||||
div.header div.title {
|
||||
background: var(--dark);
|
||||
}
|
||||
|
||||
div.header div.title a {
|
||||
display: block;
|
||||
background: url("/static/logo.png") no-repeat;
|
||||
@ -326,7 +334,7 @@ div.header div.authentication-disabled {
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 821px) {
|
||||
@media (min-width: 901px) {
|
||||
div.header {
|
||||
background: var(--dark);
|
||||
align-items: center;
|
||||
@ -509,7 +517,7 @@ section > details ul {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
@media (max-width: 820px) {
|
||||
@media (max-width: 900px) {
|
||||
section > details summary {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
@ -578,7 +586,7 @@ table#attached-devices tr.reserved td {
|
||||
background-color: #ffe9e9;
|
||||
}
|
||||
|
||||
@media (max-width: 820px) {
|
||||
@media (max-width: 900px) {
|
||||
table#attached-devices th.product,
|
||||
table#attached-devices td.product {
|
||||
display: none;
|
||||
@ -597,7 +605,7 @@ table#attached-devices tr.reserved td {
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 821px) {
|
||||
@media (min-width: 901px) {
|
||||
section#current-config form#config-actions {
|
||||
float: left;
|
||||
height: 2.75rem;
|
||||
@ -670,7 +678,7 @@ section#files p {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
@media (max-width: 820px) {
|
||||
@media (max-width: 900px) {
|
||||
section#files table#images tr th:nth-child(2),
|
||||
section#files table#images tr td:nth-child(2) {
|
||||
display: none;
|
||||
@ -684,7 +692,7 @@ section#files p {
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 821px) {
|
||||
@media (min-width: 901px) {
|
||||
section#files table#images form.file-copy input[type="submit"],
|
||||
section#files table#images form.file-rename input[type="submit"],
|
||||
section#files table#images form.file-delete input[type="submit"],
|
||||
@ -740,7 +748,7 @@ section#attach-devices form {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (max-width: 820px) {
|
||||
@media (max-width: 900px) {
|
||||
section#attach-devices table tr th:nth-child(2),
|
||||
section#attach-devices table tr td:nth-child(2) {
|
||||
display: none;
|
||||
@ -779,8 +787,12 @@ section#logging div:first-of-type {
|
||||
Index > Section: System
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
@media (min-width: 821px) {
|
||||
section#system input[type="submit"] {
|
||||
section#system div.power-control {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
@media (min-width: 901px) {
|
||||
section#system div.power-control input[type="submit"] {
|
||||
background: var(--danger);
|
||||
border-color: var(--danger);
|
||||
color: #fff;
|
||||
|
@ -77,8 +77,7 @@
|
||||
</div>
|
||||
|
||||
<div class="hostname">
|
||||
<span>{{ _("IP") }}: {{ env["ip_addr"] }}</span>
|
||||
<span>{{ _("Hostname") }}: {{ env["host"] }}</span>
|
||||
<span>{{ env['system_name'] }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -126,10 +125,13 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
{{ _("RaSCSI Reloaded version: ") }}<b>{{ env["version"] }} <a href="https://github.com/akuker/RASCSI/commit/{{ env["running_env"]["git"] }}" target="_blank">{{ env["running_env"]["git"][:7] }}</a></b>
|
||||
{{ _("RaSCSI Reloaded version:") }} <b>{{ env["version"] }} <a href="https://github.com/akuker/RASCSI/commit/{{ env["running_env"]["git"] }}" target="_blank">{{ env["running_env"]["git"][:7] }}</a></b>
|
||||
</div>
|
||||
<div>
|
||||
{{ _("Hardware and OS: ") }}{{ env["running_env"]["env"] }}
|
||||
{{ _("Hardware and OS:") }} {{ env["running_env"]["env"] }}
|
||||
</div>
|
||||
<div>
|
||||
{{ _("Network Address:") }} {{ env["host"] }} ({{ env["ip_addr"] }})
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
@ -726,16 +726,30 @@
|
||||
{{ _("System Operations") }}
|
||||
</summary>
|
||||
<ul>
|
||||
<li>{{ _("For System Name we are using the high-level \"pretty\" hostname.") }}</li>
|
||||
<li>{{ _("IMPORTANT: Always shut down the system before turning off the power. Failing to do so may lead to data loss.") }}</li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
<div>
|
||||
<form action="/sys/rename" method="post">
|
||||
<label for="system_name">{{ _("System Name:") }}</label>
|
||||
<input name="system_name" id="system_name" type="text" maxlength=120 required>
|
||||
<input type="submit" value="{{ _("Rename") }}">
|
||||
</form>
|
||||
<form action="/sys/rename" method="post">
|
||||
<input name="system_name" type="hidden" value="">
|
||||
<input type="submit" value="{{ _("Reset") }}">
|
||||
</form>
|
||||
</div>
|
||||
<div class="power-control">
|
||||
<form action="/sys/reboot" method="post" onclick="if (confirm('{{ _("Reboot the System?") }}')) shutdownNotify('{{ _("Rebooting the system...") }}'); else event.preventDefault();">
|
||||
<input type="submit" value="{{ _("Reboot System") }}">
|
||||
</form>
|
||||
<form action="/sys/shutdown" method="post" onclick="if (confirm('{{ _("Shut down the System?") }}')) shutdownNotify('{{ _("Shutting down the system...") }}'); else event.preventDefault();">
|
||||
<form action="/sys/shutdown" method="post" onclick="if (confirm('{{ _("Shut Down the System?") }}')) shutdownNotify('{{ _("Shutting down the system...") }}'); else event.preventDefault();">
|
||||
<input type="submit" value="{{ _("Shut Down System") }}">
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<hr/>
|
||||
|
@ -94,6 +94,7 @@ def get_env_info():
|
||||
"logged_in": username and auth_active(AUTH_GROUP)["status"],
|
||||
"ip_addr": ip_addr,
|
||||
"host": host,
|
||||
"system_name": sys_cmd.get_pretty_host(),
|
||||
"free_disk_space": int(sys_cmd.disk_space()["free"] / 1024 / 1024),
|
||||
"locale": get_locale(),
|
||||
"version": server_info["version"],
|
||||
@ -797,6 +798,25 @@ def release_id():
|
||||
return response(error=True, message=process["msg"])
|
||||
|
||||
|
||||
@APP.route("/sys/rename", methods=["POST"])
|
||||
@login_required
|
||||
def rename_system():
|
||||
"""
|
||||
Changes the hostname of the system
|
||||
"""
|
||||
name = str(request.form.get("system_name"))
|
||||
max_length = 120
|
||||
|
||||
if len(name) <= max_length:
|
||||
process = sys_cmd.set_pretty_host(name)
|
||||
if process:
|
||||
if name:
|
||||
return response(message=_("System name changed to '%(name)s'.", name=name))
|
||||
return response(message=_("System name reset to default."))
|
||||
|
||||
return response(error=True, message=_("Failed to change system name."))
|
||||
|
||||
|
||||
@APP.route("/sys/reboot", methods=["POST"])
|
||||
@login_required
|
||||
def restart():
|
||||
|
@ -196,3 +196,49 @@ def test_set_theme_via_query_string(http_client, theme):
|
||||
assert response.status_code == 200
|
||||
assert response_data["status"] == STATUS_SUCCESS
|
||||
assert response_data["messages"][0]["message"] == f"Theme changed to '{theme}'."
|
||||
|
||||
|
||||
# route("/sys/rename", methods=["POST"])
|
||||
def test_rename_system(env, http_client):
|
||||
new_name = "SYSTEM NAME TEST"
|
||||
|
||||
response = http_client.get("/env")
|
||||
response_data = response.json()
|
||||
|
||||
old_name = response_data["data"]["system_name"]
|
||||
|
||||
response = http_client.post(
|
||||
"/sys/rename",
|
||||
data={
|
||||
"system_name": new_name,
|
||||
},
|
||||
)
|
||||
|
||||
response_data = response.json()
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response_data["status"] == STATUS_SUCCESS
|
||||
assert response_data["messages"][0]["message"] == f"System name changed to '{new_name}'."
|
||||
|
||||
response = http_client.get("/env")
|
||||
response_data = response.json()
|
||||
|
||||
assert response_data["data"]["system_name"] == new_name
|
||||
|
||||
response = http_client.post(
|
||||
"/sys/rename",
|
||||
data={
|
||||
"system_name": old_name,
|
||||
},
|
||||
)
|
||||
|
||||
response_data = response.json()
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response_data["status"] == STATUS_SUCCESS
|
||||
assert response_data["messages"][0]["message"] == f"System name changed to '{old_name}'."
|
||||
|
||||
response = http_client.get("/env")
|
||||
response_data = response.json()
|
||||
|
||||
assert response_data["data"]["system_name"] == old_name
|
||||
|
Loading…
x
Reference in New Issue
Block a user