mirror of
https://github.com/akuker/RASCSI.git
synced 2025-01-21 13:33:20 +00:00
Introduce info.html template and use it to render detailed info (#863)
new: - new templates to render structured info contents in - get_diskinfo() class method that calls disktype and returns the results - /diskinfo endpoint in the Flask app that renders the results from get_diskinfo() changed: - /logs/show and /scsi/info endpoints in the Flask app render in templates - Now using the "RaSCSI Reloaded Control Page" header to function as the link back to the homepage (instead of the github project) which is in line with how most webapps work - Removed the center style for "Attached!" to allow the ? button to be placed on the same line - Remove individual device info, and introduced show all device info in a template
This commit is contained in:
parent
edbaaf645d
commit
5da3d6c24b
@ -161,7 +161,21 @@ class SysCmds:
|
||||
process = run(
|
||||
["journalctl"] + line_param + scope_param,
|
||||
capture_output=True,
|
||||
check=True,
|
||||
)
|
||||
if process.returncode == 0:
|
||||
return process.returncode, process.stdout.decode("utf-8")
|
||||
|
||||
return process.returncode, process.stderr.decode("utf-8")
|
||||
|
||||
@staticmethod
|
||||
def get_diskinfo(file_path):
|
||||
"""
|
||||
Takes (str) file_path path to image file to inspect.
|
||||
Returns either the disktype output, or the stderr output.
|
||||
"""
|
||||
process = run(
|
||||
["disktype", file_path],
|
||||
capture_output=True,
|
||||
)
|
||||
if process.returncode == 0:
|
||||
return process.returncode, process.stdout.decode("utf-8")
|
||||
|
@ -63,7 +63,7 @@
|
||||
<tbody>
|
||||
<tr align="center">
|
||||
<td>
|
||||
<a href="http://github.com/akuker/RASCSI" target="_blank">
|
||||
<a href="/">
|
||||
<h1>{{ _("RaSCSI Reloaded Control Page") }}</h1>
|
||||
</a>
|
||||
</td>
|
||||
|
57
python/web/src/templates/deviceinfo.html
Normal file
57
python/web/src/templates/deviceinfo.html
Normal file
@ -0,0 +1,57 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h3>{{ _("Detailed Info for Attached Devices") }}</h3>
|
||||
{% for device in devices %}
|
||||
<p>
|
||||
<table border="black" cellpadding="3">
|
||||
<tr>
|
||||
<td><i>{{ _("SCSI ID") }}</i></td>
|
||||
<td>{{ device["id"] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>{{ _("LUN") }}</i></td>
|
||||
<td>{{ device["unit"] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>{{ _("Type") }}</i></td>
|
||||
<td>{{ device["device_type"] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>{{ _("Status") }}</i></td>
|
||||
<td>{{ device["status"] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>{{ _("File") }}</i></td>
|
||||
<td>{{ device["image"] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>{{ _("Parameters") }}</i></td>
|
||||
<td>{{ device["params"] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>{{ _("Vendor") }}</i></td>
|
||||
<td>{{ device["vendor"] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>{{ _("Product") }}</i></td>
|
||||
<td>{{ device["product"] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>{{ _("Revision") }}</i></td>
|
||||
<td>{{ device["revision"] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>{{ _("Block Size") }}</i></td>
|
||||
<td>{{ device["block_size"] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>{{ _("Image Size") }}</i></td>
|
||||
<td>{{ device["size"] }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</p>
|
||||
{% endfor %}
|
||||
<p><a href="/">{{ _("Go to Home") }}</a></p>
|
||||
|
||||
{% endblock content %}
|
8
python/web/src/templates/diskinfo.html
Normal file
8
python/web/src/templates/diskinfo.html
Normal file
@ -0,0 +1,8 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h3>{{ _("Disk Image Details: %(file_name)s", file_name=file_name) }}</h3>
|
||||
<p><pre>{{ diskinfo }}</pre></p>
|
||||
<p><a href="/">{{ _("Go to Home") }}</a></p>
|
||||
|
||||
{% endblock content %}
|
@ -1,7 +1,6 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<p><a href="/">{{ _("Cancel") }}</a></p>
|
||||
<h2>{{ _("Disclaimer") }}</h2>
|
||||
<p>{{ _("These device profiles are provided as-is with no guarantee to work equally to the actual physical device they are named after. You may need to provide appropirate device drivers and/or configuration parameters for them to function properly. If you would like to see data modified, or have additional devices to add to the list, please raise an issue ticket at <a href=\"%(url)s\">GitHub</a>.", url="https://github.com/akuker/RASCSI/issues") }}</p>
|
||||
<h2>{{ _("Hard Drives") }}</h2>
|
||||
@ -135,7 +134,7 @@
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<p><small>{{ _("%(disk_space)s MiB disk space remaining on the Pi", disk_space=env["free_disk_space"]) }}</small></p>
|
||||
<p><a href="/">{{ _("Cancel") }}</a></p>
|
||||
<p><small>{{ _("%(disk_space)s MiB disk space remaining on the Pi", disk_space=env["free_disk_space"]) }}</small></p>
|
||||
<p><a href="/">{{ _("Go to Home") }}</a></p>
|
||||
|
||||
{% endblock content %}
|
||||
|
@ -106,11 +106,6 @@
|
||||
<input name="unit" type="hidden" value="{{ device.unit }}">
|
||||
<input type="submit" value="{{ _("Detach") }}">
|
||||
</form>
|
||||
<form action="/scsi/info" method="post">
|
||||
<input name="scsi_id" type="hidden" value="{{ device.id }}">
|
||||
<input name="unit" type="hidden" value="{{ device.unit }}">
|
||||
<input type="submit" value="{{ _("Info") }}">
|
||||
</form>
|
||||
{% else %}
|
||||
<form action="/scsi/reserve" method="post" onsubmit="var memo = prompt('{{ _("Enter a memo for this reservation") }}'); if (memo === null) event.preventDefault(); document.getElementById('memo_{{ device.id }}').value = memo;">
|
||||
<input name="scsi_id" type="hidden" value="{{ device.id }}">
|
||||
@ -140,9 +135,20 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p><form action="/scsi/detach_all" method="post" onsubmit="return confirm('{{ _("Detach all SCSI Devices?") }}')">
|
||||
<input type="submit" value="{{ _("Detach All Devices") }}">
|
||||
</form></p>
|
||||
<table style="border: none;" cellpadding="3">
|
||||
<tr style="border: none;">
|
||||
<td style="border: none;">
|
||||
<form action="/scsi/detach_all" method="post" onsubmit="return confirm('{{ _("Detach all SCSI Devices?") }}')">
|
||||
<input type="submit" value="{{ _("Detach All Devices") }}">
|
||||
</form>
|
||||
</td>
|
||||
<td style="border: none;">
|
||||
<form action="/scsi/info" method="post">
|
||||
<input type="submit" value="{{ _("Show Device Info") }}">
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -243,9 +249,7 @@
|
||||
</td>
|
||||
<td>
|
||||
{% if file["name"] in attached_images %}
|
||||
<center>
|
||||
{{ _("Attached!") }}
|
||||
</center>
|
||||
{{ _("In use") }}
|
||||
{% else %}
|
||||
{% if file["archive_contents"] %}
|
||||
<form action="/files/extract_image" method="post">
|
||||
@ -303,6 +307,12 @@
|
||||
<input type="submit" value="{{ _("Delete") }}">
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if not file["archive_contents"] %}
|
||||
<form action="/files/diskinfo" method="post">
|
||||
<input name="file_name" type="hidden" value="{{ file['name'] }}">
|
||||
<input type="submit" value="{{ _("?") }}">
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@ -393,8 +403,8 @@
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
<table style="border: none">
|
||||
<tr style="border: none">
|
||||
<table style="border: none;">
|
||||
<tr style="border: none;">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form name="dropper" action="/files/upload" method="post" class="dropzone dz-clickable" enctype="multipart/form-data" id="dropper"></form>
|
||||
</td>
|
||||
|
8
python/web/src/templates/logs.html
Normal file
8
python/web/src/templates/logs.html
Normal file
@ -0,0 +1,8 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h3>{{ _("System Logs: %(scope)s %(lines)s lines", scope=scope, lines=lines) }}</h3>
|
||||
<p><pre>{{ logs }}</pre></p>
|
||||
<p><a href="/">{{ _("Go to Home") }}</a></p>
|
||||
|
||||
{% endblock content %}
|
@ -217,11 +217,6 @@ def index():
|
||||
server_info["sccd"]
|
||||
)
|
||||
|
||||
if "username" in session:
|
||||
username = session["username"]
|
||||
else:
|
||||
username = None
|
||||
|
||||
return response(
|
||||
template="index.html",
|
||||
locales=get_supported_locales(),
|
||||
@ -310,7 +305,7 @@ def drive_list():
|
||||
server_info = ractl_cmd.get_server_info()
|
||||
|
||||
return response(
|
||||
"drives.html",
|
||||
template="drives.html",
|
||||
files=file_cmd.list_images()["files"],
|
||||
base_dir=server_info["image_dir"],
|
||||
hd_conf=hd_conf,
|
||||
@ -479,6 +474,33 @@ def config_load():
|
||||
return response(error=True, message="Action field (load, delete) missing")
|
||||
|
||||
|
||||
@APP.route("/files/diskinfo", methods=["POST"])
|
||||
def show_diskinfo():
|
||||
"""
|
||||
Displays disk image info
|
||||
"""
|
||||
file_name = request.form.get("file_name")
|
||||
|
||||
server_info = ractl_cmd.get_server_info()
|
||||
returncode, diskinfo = sys_cmd.get_diskinfo(
|
||||
server_info["image_dir"] +
|
||||
"/" +
|
||||
file_name
|
||||
)
|
||||
if returncode == 0:
|
||||
return response(
|
||||
template="diskinfo.html",
|
||||
file_name=file_name,
|
||||
diskinfo=diskinfo,
|
||||
version=server_info["version"],
|
||||
)
|
||||
|
||||
return response(
|
||||
error=True,
|
||||
message=_("An error occurred when getting disk info: %(error)s", error=diskinfo)
|
||||
)
|
||||
|
||||
|
||||
@APP.route("/logs/show", methods=["POST"])
|
||||
def show_logs():
|
||||
"""
|
||||
@ -487,16 +509,21 @@ def show_logs():
|
||||
lines = request.form.get("lines")
|
||||
scope = request.form.get("scope")
|
||||
|
||||
# TODO: Render logs in a template (issue #836) and structured JSON
|
||||
returncode, logs = sys_cmd.get_logs(lines, scope)
|
||||
if returncode == 0:
|
||||
headers = {"content-type": "text/plain"}
|
||||
return logs, headers
|
||||
server_info = ractl_cmd.get_server_info()
|
||||
return response(
|
||||
template="logs.html",
|
||||
scope=scope,
|
||||
lines=lines,
|
||||
logs=logs,
|
||||
version=server_info["version"],
|
||||
)
|
||||
|
||||
return response(error=True, message=[
|
||||
(_("An error occurred when fetching logs."), "error"),
|
||||
(logs, "stderr"),
|
||||
])
|
||||
return response(
|
||||
error=True,
|
||||
message=_("An error occurred when fetching logs: %(error)s", error=logs)
|
||||
)
|
||||
|
||||
|
||||
@APP.route("/logs/level", methods=["POST"])
|
||||
@ -684,56 +711,18 @@ def eject():
|
||||
@APP.route("/scsi/info", methods=["POST"])
|
||||
def device_info():
|
||||
"""
|
||||
Displays detailed info for a specific device
|
||||
Displays detailed info for all attached devices
|
||||
"""
|
||||
scsi_id = request.form.get("scsi_id")
|
||||
unit = request.form.get("unit")
|
||||
server_info = ractl_cmd.get_server_info()
|
||||
process = ractl_cmd.list_devices()
|
||||
if process["status"]:
|
||||
return response(
|
||||
template="deviceinfo.html",
|
||||
devices=process["device_list"],
|
||||
version=server_info["version"],
|
||||
)
|
||||
|
||||
devices = ractl_cmd.list_devices(scsi_id, unit)
|
||||
|
||||
# First check if any device at all was returned
|
||||
if not devices["status"]:
|
||||
return response(error=True, message=devices["msg"])
|
||||
# Looking at the first dict in list to get
|
||||
# the one and only device that should have been returned
|
||||
device = devices["device_list"][0]
|
||||
if str(device["id"]) == scsi_id:
|
||||
# TODO: Move the device info to the template instead of a flash message
|
||||
message = "\n".join([
|
||||
_("DEVICE INFO"),
|
||||
"===========",
|
||||
_("SCSI ID: %(id_number)s", id_number=device["id"]),
|
||||
_("LUN: %(unit_number)s", unit_number=device["unit"]),
|
||||
_("Type: %(device_type)s", device_type=device["device_type"]),
|
||||
_("Status: %(device_status)s", device_status=device["status"]),
|
||||
_("File: %(image_file)s", image_file=device["image"]),
|
||||
_("Parameters: %(value)s", value=device["params"]),
|
||||
_("Vendor: %(value)s", value=device["vendor"]),
|
||||
_("Product: %(value)s", value=device["product"]),
|
||||
_("Revision: %(revision_number)s", revision_number=device["revision"]),
|
||||
_("Block Size: %(value)s bytes", value=device["block_size"]),
|
||||
_("Image Size: %(value)s bytes", value=device["size"]),
|
||||
])
|
||||
|
||||
# Don't send redundant "info" message with the JSON response
|
||||
if request.headers.get("accept") == "application/json":
|
||||
return response(device_info={
|
||||
"scsi_id": device["id"],
|
||||
"lun": device["unit"],
|
||||
"device_type": device["device_type"],
|
||||
"status": device["status"],
|
||||
"file": device["image"],
|
||||
"parameters": device["params"],
|
||||
"vendor": device["vendor"],
|
||||
"product": device["product"],
|
||||
"revision": device["revision"],
|
||||
"block_size": device["block_size"],
|
||||
"size": device["size"],
|
||||
})
|
||||
|
||||
return response(message=[(message, "info")])
|
||||
|
||||
return response(error=True, message=devices["msg"])
|
||||
return response(error=True, message=_("No devices attached"))
|
||||
|
||||
|
||||
@APP.route("/scsi/reserve", methods=["POST"])
|
||||
|
@ -176,17 +176,14 @@ def test_show_device_info(http_client, create_test_image, detach_devices):
|
||||
|
||||
response = http_client.post(
|
||||
"/scsi/info",
|
||||
data={
|
||||
"scsi_id": SCSI_ID,
|
||||
},
|
||||
)
|
||||
|
||||
response_data = response.json()
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response_data["status"] == STATUS_SUCCESS
|
||||
assert "device_info" in response_data["data"]
|
||||
assert response_data["data"]["device_info"]["file"] == f"{IMAGES_DIR}/{test_image}"
|
||||
assert "devices" in response_data["data"]
|
||||
assert response_data["data"]["devices"][0]["file"] == test_image
|
||||
|
||||
# Cleanup
|
||||
detach_devices()
|
||||
|
@ -315,3 +315,20 @@ def test_download_url_to_iso(
|
||||
# Cleanup
|
||||
detach_devices()
|
||||
delete_file(iso_file_name)
|
||||
|
||||
|
||||
# route("/files/diskinfo", methods=["POST"])
|
||||
def test_show_diskinfo(http_client, create_test_image):
|
||||
test_image = create_test_image()
|
||||
|
||||
response = http_client.post(
|
||||
"/files/diskinfo",
|
||||
data={
|
||||
"file_name": test_image,
|
||||
},
|
||||
)
|
||||
|
||||
response_data = response.json()
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Regular file" in response_data["data"]["diskinfo"]
|
||||
|
@ -61,12 +61,15 @@ def test_show_logs(http_client):
|
||||
"/logs/show",
|
||||
data={
|
||||
"lines": 100,
|
||||
"scope": "",
|
||||
"scope": "rascsi",
|
||||
},
|
||||
)
|
||||
|
||||
response_data = response.json()
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.headers["content-type"] == "text/plain"
|
||||
assert response_data["data"]["lines"] == "100"
|
||||
assert response_data["data"]["scope"] == "rascsi"
|
||||
|
||||
|
||||
# route("/config/save", methods=["POST"])
|
||||
|
Loading…
x
Reference in New Issue
Block a user