diff --git a/python/common/src/rascsi/sys_cmds.py b/python/common/src/rascsi/sys_cmds.py index 4b6736f6..46971d8a 100644 --- a/python/common/src/rascsi/sys_cmds.py +++ b/python/common/src/rascsi/sys_cmds.py @@ -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") diff --git a/python/web/src/templates/base.html b/python/web/src/templates/base.html index 2eb304de..e8a21057 100644 --- a/python/web/src/templates/base.html +++ b/python/web/src/templates/base.html @@ -63,7 +63,7 @@ - +

{{ _("RaSCSI Reloaded Control Page") }}

diff --git a/python/web/src/templates/deviceinfo.html b/python/web/src/templates/deviceinfo.html new file mode 100644 index 00000000..7371815f --- /dev/null +++ b/python/web/src/templates/deviceinfo.html @@ -0,0 +1,57 @@ +{% extends "base.html" %} + +{% block content %} +

{{ _("Detailed Info for Attached Devices") }}

+{% for device in devices %} +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ _("SCSI ID") }}{{ device["id"] }}
{{ _("LUN") }}{{ device["unit"] }}
{{ _("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"] }}
{{ _("Image Size") }}{{ device["size"] }}
+

+{% endfor %} +

{{ _("Go to Home") }}

+ +{% endblock content %} diff --git a/python/web/src/templates/diskinfo.html b/python/web/src/templates/diskinfo.html new file mode 100644 index 00000000..6b41fa3b --- /dev/null +++ b/python/web/src/templates/diskinfo.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} + +{% block content %} +

{{ _("Disk Image Details: %(file_name)s", file_name=file_name) }}

+

{{ diskinfo }}

+

{{ _("Go to Home") }}

+ +{% endblock content %} diff --git a/python/web/src/templates/drives.html b/python/web/src/templates/drives.html index a9cdad95..f49699a8 100644 --- a/python/web/src/templates/drives.html +++ b/python/web/src/templates/drives.html @@ -1,7 +1,6 @@ {% extends "base.html" %} {% block content %} -

{{ _("Cancel") }}

{{ _("Disclaimer") }}

{{ _("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 GitHub.", url="https://github.com/akuker/RASCSI/issues") }}

{{ _("Hard Drives") }}

@@ -135,7 +134,7 @@ {% endfor %} -

{{ _("%(disk_space)s MiB disk space remaining on the Pi", disk_space=env["free_disk_space"]) }}

-

{{ _("Cancel") }}

+

{{ _("%(disk_space)s MiB disk space remaining on the Pi", disk_space=env["free_disk_space"]) }}

+

{{ _("Go to Home") }}

{% endblock content %} diff --git a/python/web/src/templates/index.html b/python/web/src/templates/index.html index a120161f..24568413 100644 --- a/python/web/src/templates/index.html +++ b/python/web/src/templates/index.html @@ -106,11 +106,6 @@ -
- - - -
{% else %}
@@ -140,9 +135,20 @@ -

- -

+ + + + + +
+
+ +
+
+
+ +
+

@@ -243,9 +249,7 @@ {% if file["name"] in attached_images %} -
- {{ _("Attached!") }} -
+ {{ _("In use") }} {% else %} {% if file["archive_contents"] %}
@@ -303,6 +307,12 @@
{% endif %} + {% if not file["archive_contents"] %} +
+ + +
+ {% endif %} {% endfor %} @@ -393,8 +403,8 @@ - - +
+ diff --git a/python/web/src/templates/logs.html b/python/web/src/templates/logs.html new file mode 100644 index 00000000..474f9979 --- /dev/null +++ b/python/web/src/templates/logs.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} + +{% block content %} +

{{ _("System Logs: %(scope)s %(lines)s lines", scope=scope, lines=lines) }}

+

{{ logs }}

+

{{ _("Go to Home") }}

+ +{% endblock content %} diff --git a/python/web/src/web.py b/python/web/src/web.py index 2af3d135..d53d93d6 100644 --- a/python/web/src/web.py +++ b/python/web/src/web.py @@ -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"]) diff --git a/python/web/tests/api/test_devices.py b/python/web/tests/api/test_devices.py index 640f20bd..81c7d4ea 100644 --- a/python/web/tests/api/test_devices.py +++ b/python/web/tests/api/test_devices.py @@ -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() diff --git a/python/web/tests/api/test_files.py b/python/web/tests/api/test_files.py index cf0d96d9..81f7d381 100644 --- a/python/web/tests/api/test_files.py +++ b/python/web/tests/api/test_files.py @@ -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"] diff --git a/python/web/tests/api/test_settings.py b/python/web/tests/api/test_settings.py index 9e7c276a..12a03aa8 100644 --- a/python/web/tests/api/test_settings.py +++ b/python/web/tests/api/test_settings.py @@ -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"])