diff --git a/src/web/ractl_cmds.py b/src/web/ractl_cmds.py index 935ee3c7..b091f196 100644 --- a/src/web/ractl_cmds.py +++ b/src/web/ractl_cmds.py @@ -5,6 +5,15 @@ import rascsi_interface_pb2 as proto def get_server_info(): + """ + Sends a SERVER_INFO command to the server. + Returns a dict with: + - boolean status + - str version (RaSCSI version number) + - list of str log_levels (the log levels RaSCSI supports) + - str current_log_level + - list of int reserved_ids + """ command = proto.PbCommand() command.operation = proto.PbOperation.SERVER_INFO @@ -17,10 +26,22 @@ def get_server_info(): log_levels = result.server_info.log_levels current_log_level = result.server_info.current_log_level reserved_ids = list(result.server_info.reserved_ids) - return {"status": result.status, "version": version, "log_levels": log_levels, "current_log_level": current_log_level, "reserved_ids": reserved_ids} + return { + "status": result.status, + "version": version, + "log_levels": log_levels, + "current_log_level": current_log_level, + "reserved_ids": reserved_ids + } def get_network_info(): + """ + Sends a NETWORK_INTERFACES_INFO command to the server. + Returns a dict with: + - boolean status + - list of str ifs (network interfaces detected by RaSCSI) + """ command = proto.PbCommand() command.operation = proto.PbOperation.NETWORK_INTERFACES_INFO @@ -32,6 +53,12 @@ def get_network_info(): def get_device_types(): + """ + Sends a DEVICE_TYPES_INFO command to the server. + Returns a dict with: + - boolean status + - list of str device_types (device types that RaSCSI supports, ex. SCHD, SCCD, etc) + """ command = proto.PbCommand() command.operation = proto.PbOperation.DEVICE_TYPES_INFO @@ -45,6 +72,12 @@ def get_device_types(): def validate_scsi_id(scsi_id): + """ + Checks that scsi_id is a valid SCSI ID, i.e. a number between 0 and 7. + Returns a dict with: + - boolean status + - str msg (result message) + """ from re import match if match("[0-7]", str(scsi_id)) != None: return {"status": True, "msg": "Valid SCSI ID."} @@ -53,6 +86,10 @@ def validate_scsi_id(scsi_id): def get_valid_scsi_ids(devices, reserved_ids): + """ + Takes a list of dicts devices, and list of ints reserved_ids. + Returns a list of ints valid_ids, which are the SCSI ids where it is possible to attach a device. + """ occupied_ids = [] for d in devices: # Make it possible to insert images on top of a @@ -69,12 +106,23 @@ def get_valid_scsi_ids(devices, reserved_ids): except: # May reach this state if the RaSCSI Web UI thinks an ID # is reserved but RaSCSI has not actually reserved it. - logging.warning(f"SCSI ID {id} flagged as both valid and invalid. Try restarting the RaSCSI Web UI.") + logging.warning(f"SCSI ID {id} flagged as both valid and invalid. \ + Try restarting the RaSCSI Web UI.") valid_ids.reverse() return valid_ids def attach_image(scsi_id, **kwargs): + """ + Takes int scsi_id and kwargs containing 0 or more device properties + + If the current attached device is a removable device wihout media inserted, + this sends a INJECT command to the server. + If there is no currently attached device, this sends the ATTACH command to the server. + + Returns boolean status and return message str msg + + """ command = proto.PbCommand() devices = proto.PbDeviceDefinition() devices.id = int(scsi_id) @@ -130,6 +178,10 @@ def attach_image(scsi_id, **kwargs): def detach_by_id(scsi_id): + """ + Takes int scsi_id and sends a DETACH command to the server. + Returns boolean status and return message str msg. + """ devices = proto.PbDeviceDefinition() devices.id = int(scsi_id) @@ -144,6 +196,10 @@ def detach_by_id(scsi_id): def detach_all(): + """ + Sends a DETACH_ALL command to the server. + Returns boolean status and return message str msg. + """ command = proto.PbCommand() command.operation = proto.PbOperation.DETACH_ALL @@ -154,6 +210,10 @@ def detach_all(): def eject_by_id(scsi_id): + """ + Takes int scsi_id and sends an EJECT command to the server. + Returns boolean status and return message str msg. + """ devices = proto.PbDeviceDefinition() devices.id = int(scsi_id) @@ -168,6 +228,13 @@ def eject_by_id(scsi_id): def list_devices(scsi_id=None): + """ + Takes optional int scsi_id and sends a DEVICES_INFO command to the server. + If no scsi_id is provided, returns a list of dicts of all attached devices. + If scsi_id is is provided, returns a list of one dict for the given device. + If no attached device is found, returns an empty list. + Returns bolean status, list of dicts device_list + """ from os import path command = proto.PbCommand() command.operation = proto.PbOperation.DEVICES_INFO @@ -226,6 +293,11 @@ def list_devices(scsi_id=None): def sort_and_format_devices(devices): + """ + Takes a list of dicts devices and returns a list of dicts. + Sorts by SCSI ID acending (0 to 7). + For SCSI IDs where no device is attached, inject a dict with placeholder text. + """ occupied_ids = [] for d in devices: occupied_ids.append(d["id"]) @@ -244,7 +316,11 @@ def sort_and_format_devices(devices): def set_log_level(log_level): - '''Sends a command to the server to change the log level. Takes target log level as an argument.''' + """ + Sends a LOG_LEVEL command to the server. + Takes str log_level as an argument. + Returns bolean status and str msg. + """ command = proto.PbCommand() command.operation = proto.PbOperation.LOG_LEVEL command.params["level"] = str(log_level) @@ -256,6 +332,10 @@ def set_log_level(log_level): def send_pb_command(payload): + """ + Takes a str containing a serialized protobuf as argument. + Establishes a socket connection with RaSCSI. + """ # Host and port number where rascsi is listening for socket connections HOST = 'localhost' PORT = 6868 @@ -285,6 +365,13 @@ def send_pb_command(payload): def send_over_socket(s, payload): + """ + Takes a socket object and str payload with serialized protobuf. + Sends payload to RaSCSI over socket and captures the response. + Tries to extract and interpret the protobuf header to get response size. + Reads data from socket in 2048 bytes chunks until all data is received. + + """ from struct import pack, unpack # Prepending a little endian 32bit header with the message size @@ -303,13 +390,20 @@ def send_over_socket(s, payload): chunk = s.recv(min(response_length - bytes_recvd, 2048)) if chunk == b'': from flask import abort - logging.error("Read an empty chunk from the socket. Socket connection may have dropped unexpectedly. Check if RaSCSI has crashed.") - abort(503, "Lost connection to RaSCSI. Please go back and try again. If the issue persists, please report a bug.") + logging.error("Read an empty chunk from the socket. \ + Socket connection has dropped unexpectedly. \ + RaSCSI may have has crashed.") + abort(503, "Lost connection to RaSCSI. \ + Please go back and try again. \ + If the issue persists, please report a bug.") chunks.append(chunk) bytes_recvd = bytes_recvd + len(chunk) response_message = b''.join(chunks) return response_message else: from flask import abort - logging.error("The response from RaSCSI did not contain a protobuf header.") - abort(500, "Did not get a valid response from RaSCSI. Please go back and try again. If the issue persists, please report a bug.") + logging.error("The response from RaSCSI did not contain a protobuf header. \ + RaSCSI may have crashed.") + abort(500, "Did not get a valid response from RaSCSI. \ + Please go back and try again. \ + If the issue persists, please report a bug.")