mirror of
https://github.com/akuker/RASCSI.git
synced 2024-06-12 00:29:29 +00:00
8a3642bf9a
* Making saving and loading config files work with protobuf * Formatted the Status column, and fixed the available ID logic * Updated handling of removed status for devices without image file support * Comment update * Fixed typo * Updated logging * Updated handling of removed status for devices without image file support * Comment update * Fixed typo * Updated logging * Better handling of device status * Updated parameter handling * Updated setting default interfaces * Revert "Updated setting default interfaces" This reverts commit210abc775d
. * Revert "Updated parameter handling" This reverts commit35302addd5
. * Abort with a 404 if rascsi is not running. Use any protobuf response to determine whether rascsi is running (should hardly be required anymore due to the other change, but just in case). * Move id reservation back into __main__ * Remove check for device type when validating Removed image * Leverage device property data for better status messages * Remove redundant string sanitation when reading config csv file * Clean up device list generation * Cleanup * Remove duplicates before building valid scsi id list * Fully translated cfilesystem.h code comments to English; partially translated cfilesystem.cpp * rascsi supports reserving IDs * Updated help message * Replaced BOOL by bool * Logging update * Logging update * Cleanup * Restructure the easyinstall.sh script to combine the install/update flows, and disallow installing the webapp by itself * Remove redundant steps handled in Makefile * Add the functionality to specify connect_type through a parameter * Add validation to the argument parser allowing only STANDARD and FULLSPEC as options * Complete translation of code comments for cfilesystem.h; partial translation for cfilesystem.cpp * Cleanup * Merge parts of the Network Assistant script by sonique6784; fix the run_choice startup parameter * Improve on the network setup messages * Fix routing address * Add checks for previous configuration; cleanup * Cleanup * Remove redundant step in wired setup. Improve messages. * Cleanup * Added default parameters to device properties * Return parameters a device was set up with * Add flows for configuring custom network settings; adopting some logic by sonique6784 * Improved device initialization * Updated default parameter handling * Updated default parameter handling * Fixed typo * Comment updates * Comment update * Make iso generation work again, and add error handling to urllib actions * Manage default parameters in the respective device * Print available network interfaces. Clean up step and improve descriptive messages. * Make the script clean up previous configurations * Make the script only show relevant interfaces * Partial translation of cfilesystem.cpp * Do not pass empty parameter string * Added supports_params flag * Completely translate code comments in cfilesystem.cpp * Show rascsi-web status after installing * Refactoring * Made comparisons more consistent * Updated error handling * Updated exception handling * Made comparisons more consistent * Updated error handling * Overlooked code comment translation * Renaming * Better error handling for socket connection * Disable two NEC hd image types due to issue#232 * Comment update * NEC sectors size must be 512 bytes * Updated logging * Updated vendor name handling * Updated handling of media loading/unloading * Comment update * NEC sectors size must be 512 bytes * Updated logging * Updated vendor name handling * Updated handling of media loading/unloading * Better handling of removable disks in the web ui * Added stoppable property and stopped status * Made MO stoppable * Removed duplicate code * Removed duplicate code * Copy read-only property * Renaming * Add an assistant for reserving scsi ids * Don't show action if no device attached * Implement a device_info app path, and cut down on device columns always shown * Cleanup * Removed duplicate code, added START/STOP * Improved default parameter handling * Updated load/eject handling * Logging update * Fixed typo * Verified START/STOP UNIT * Updated logging * Updated status handling * Updated status handling * More status handling updates * Logging update * Made instance fields local variables * Removed duplicate code, added START/STOP * Improved default parameter handling * Updated load/eject handling * Logging update * Fixed typo * Verified START/STOP UNIT * Updated logging * Updated status handling * Updated status handling * More status handling updates * Logging update * Made instance fields local variables * Made disk_t private * Made some data structures private * Fixed ARM compile issue * Fast forward instead of rebase existing git repo * Fixed ctapdriver initialization issue * Reset read-only status when opening an image file * Cleanup * Cleanup * Made logging more consistent * Updated log level * Cleanup * Log load/eject on error level for testing * Revert "Log load/eject on error level for testing" This reverts commitd35a15ea8e
. * Assume drive is not ready after having been stopped * Updated status handling * Make the csv config files store all relevant device data for reading * Read 9 column csv config files * Fixed typo * Rebuild manpage * Fixed issue #234 (MODE SENSE (10) returns wrong mode parameter header) * Removed unused code * Enum data type update * Removed duplicate range check * Removed duplicate code * Removed more duplicate code * Logging update * SCCD sector size was not meant to be configurable * Better error handling for csv reading and writing * Updated configurable sector size properties * Removed assertion * Improved error handling * Updated error handling * Re-added special error handling only relevant for SASI * Added TODOs * Comment update * Added override modifier * Removed obsolete debug flag (related code was not called) * Comment and logging updates * Removed obsolete try/catch * Revert "Removed obsolete try/catch" This reverts commit39ca12d8b1
. * Comment update * Removed duplicate code * Updated error messages, use more foreach loops * Avoid storing RaSCSI generated product info in config file * Updated logging * Logging update * Save config files in json instead of csv * Fix bugs with json config loading * Refactoring & remove unused code * Refactoring * Display upper case file endings in file management list * Only show product vendor for non-RaSCSI devices in the device list * Translate code comment * Refactoring * Fix bad identation * Improve valid file extension handling * Add validation when attaching removable media * Display valid file endings under the file list * Cleanup * Don't store 0 block size * Fix indentation * Read and write config files in key:pair format * Add section for controlling logging * README update * Added block_count * Cleanup, fix typos * Support attaching CD-ROM with custom block size * Evaluate block size when inserting a media * rasctl display capacity if available * Info message update * Use kwargs for device attachment * Fix bugs in attach_image kwargs; make config file more readable * POC for attaching device with profile * Only list product types valid for the particular image file * Perform validation of HDD image size based on the product profile * Implement sidecar config files for drive images. * Added missing product name to NEC vital product data * MO block size depends on capacity only * Better error handling for device sidecar config loading * Extended property/status display * Property display update * Updated error handling * Handle image sizes in bytes internally * Revert change * Resolve bad merge Co-authored-by: Uwe Seimet <Uwe.Seimet@seimet.de>
329 lines
12 KiB
Python
329 lines
12 KiB
Python
import logging
|
|
|
|
from settings import *
|
|
import rascsi_interface_pb2 as proto
|
|
|
|
|
|
def get_server_info():
|
|
command = proto.PbCommand()
|
|
command.operation = proto.PbOperation.SERVER_INFO
|
|
|
|
data = send_pb_command(command.SerializeToString())
|
|
result = proto.PbResult()
|
|
result.ParseFromString(data)
|
|
version = str(result.server_info.major_version) + "." +\
|
|
str(result.server_info.minor_version) + "." +\
|
|
str(result.server_info.patch_version)
|
|
log_levels = result.server_info.log_levels
|
|
current_log_level = result.server_info.current_log_level
|
|
return {"status": result.status, "version": version, "log_levels": log_levels, "current_log_level": current_log_level}
|
|
|
|
|
|
def validate_scsi_id(scsi_id):
|
|
from re import match
|
|
if match("[0-7]", str(scsi_id)) != None:
|
|
return {"status": True, "msg": "Valid SCSI ID."}
|
|
else:
|
|
return {"status": False, "msg": "Invalid SCSI ID. Should be a number between 0-7"}
|
|
|
|
|
|
def get_valid_scsi_ids(devices, invalid_list, occupied_ids):
|
|
for device in devices:
|
|
# Make it possible to insert images on top of a
|
|
# removable media device currently without an image attached
|
|
if "No Media" in device["status"]:
|
|
occupied_ids.remove(device["id"])
|
|
|
|
# Combine lists and remove duplicates
|
|
invalid_ids = list(set(invalid_list + occupied_ids))
|
|
valid_ids = list(range(8))
|
|
for id in invalid_ids:
|
|
valid_ids.remove(int(id))
|
|
valid_ids.reverse()
|
|
return valid_ids
|
|
|
|
|
|
def get_type(scsi_id):
|
|
device = proto.PbDeviceDefinition()
|
|
device.id = int(scsi_id)
|
|
|
|
command = proto.PbCommand()
|
|
command.operation = proto.PbOperation.DEVICE_INFO
|
|
command.devices.append(device)
|
|
|
|
data = send_pb_command(command.SerializeToString())
|
|
result = proto.PbResult()
|
|
result.ParseFromString(data)
|
|
# Assuming that only one PbDevice object is present in the response
|
|
try:
|
|
result_type = proto.PbDeviceType.Name(result.device_info.devices[0].type)
|
|
return {"status": result.status, "msg": result.msg, "device_type": result_type}
|
|
except:
|
|
return {"status": result.status, "msg": result.msg, "device_type": None}
|
|
|
|
|
|
def attach_image(scsi_id, **kwargs):
|
|
|
|
# Handling the inserting of media into an attached removable type device
|
|
currently_attached = get_type(scsi_id)["device_type"]
|
|
device_type = kwargs.get("device_type", None)
|
|
|
|
if device_type in REMOVABLE_DEVICE_TYPES and currently_attached in REMOVABLE_DEVICE_TYPES:
|
|
if currently_attached != device_type:
|
|
return {"status": False, "msg": f"Cannot insert an image for {device_type} into a {currently_attached} device."}
|
|
else:
|
|
return insert(scsi_id, kwargs.get("image", ""))
|
|
# Handling attaching a new device
|
|
else:
|
|
devices = proto.PbDeviceDefinition()
|
|
devices.id = int(scsi_id)
|
|
if "device_type" in kwargs.keys():
|
|
logging.warning(kwargs["device_type"])
|
|
devices.type = proto.PbDeviceType.Value(str(kwargs["device_type"]))
|
|
if "unit" in kwargs.keys():
|
|
devices.unit = kwargs["unit"]
|
|
if "image" in kwargs.keys():
|
|
if kwargs["image"] not in [None, ""]:
|
|
devices.params.append(kwargs["image"])
|
|
if "params" in kwargs.keys():
|
|
for p in kwargs["params"]:
|
|
devices.params.append(p)
|
|
if "vendor" in kwargs.keys():
|
|
if kwargs["vendor"] not in [None, ""]:
|
|
devices.vendor = kwargs["vendor"]
|
|
if "product" in kwargs.keys():
|
|
if kwargs["product"] not in [None, ""]:
|
|
devices.product = kwargs["product"]
|
|
if "revision" in kwargs.keys():
|
|
if kwargs["revision"] not in [None, ""]:
|
|
devices.revision = kwargs["revision"]
|
|
if "block_size" in kwargs.keys():
|
|
if kwargs["block_size"] not in [None, ""]:
|
|
devices.block_size = int(kwargs["block_size"])
|
|
|
|
command = proto.PbCommand()
|
|
command.operation = proto.PbOperation.ATTACH
|
|
command.devices.append(devices)
|
|
|
|
data = send_pb_command(command.SerializeToString())
|
|
result = proto.PbResult()
|
|
result.ParseFromString(data)
|
|
return {"status": result.status, "msg": result.msg}
|
|
|
|
|
|
def detach_by_id(scsi_id):
|
|
devices = proto.PbDeviceDefinition()
|
|
devices.id = int(scsi_id)
|
|
|
|
command = proto.PbCommand()
|
|
command.operation = proto.PbOperation.DETACH
|
|
command.devices.append(devices)
|
|
|
|
data = send_pb_command(command.SerializeToString())
|
|
result = proto.PbResult()
|
|
result.ParseFromString(data)
|
|
return {"status": result.status, "msg": result.msg}
|
|
|
|
|
|
def detach_all():
|
|
command = proto.PbCommand()
|
|
command.operation = proto.PbOperation.DETACH_ALL
|
|
|
|
data = send_pb_command(command.SerializeToString())
|
|
result = proto.PbResult()
|
|
result.ParseFromString(data)
|
|
return {"status": result.status, "msg": result.msg}
|
|
|
|
|
|
def eject_by_id(scsi_id):
|
|
devices = proto.PbDeviceDefinition()
|
|
devices.id = int(scsi_id)
|
|
|
|
command = proto.PbCommand()
|
|
command.operation = proto.PbOperation.EJECT
|
|
command.devices.append(devices)
|
|
|
|
data = send_pb_command(command.SerializeToString())
|
|
result = proto.PbResult()
|
|
result.ParseFromString(data)
|
|
return {"status": result.status, "msg": result.msg}
|
|
|
|
|
|
def insert(scsi_id, image):
|
|
devices = proto.PbDeviceDefinition()
|
|
devices.id = int(scsi_id)
|
|
devices.params.append(image)
|
|
|
|
command = proto.PbCommand()
|
|
command.operation = proto.PbOperation.INSERT
|
|
command.devices.append(devices)
|
|
|
|
data = send_pb_command(command.SerializeToString())
|
|
result = proto.PbResult()
|
|
result.ParseFromString(data)
|
|
return {"status": result.status, "msg": result.msg}
|
|
|
|
|
|
def attach_daynaport(scsi_id):
|
|
devices = proto.PbDeviceDefinition()
|
|
devices.id = int(scsi_id)
|
|
devices.type = proto.PbDeviceType.SCDP
|
|
|
|
command = proto.PbCommand()
|
|
command.operation = proto.PbOperation.ATTACH
|
|
command.devices.append(devices)
|
|
|
|
data = send_pb_command(command.SerializeToString())
|
|
result = proto.PbResult()
|
|
result.ParseFromString(data)
|
|
return {"status": result.status, "msg": result.msg}
|
|
|
|
|
|
def list_devices(scsi_id=None):
|
|
from os import path
|
|
command = proto.PbCommand()
|
|
command.operation = proto.PbOperation.DEVICE_INFO
|
|
|
|
# If method is called with scsi_id parameter, return the info on those devices
|
|
# Otherwise, return the info on all attached devices
|
|
if scsi_id != None:
|
|
device = proto.PbDeviceDefinition()
|
|
device.id = int(scsi_id)
|
|
command.devices.append(device)
|
|
|
|
data = send_pb_command(command.SerializeToString())
|
|
result = proto.PbResult()
|
|
result.ParseFromString(data)
|
|
|
|
device_list = []
|
|
occupied_ids = []
|
|
n = 0
|
|
while n < len(result.device_info.devices):
|
|
did = result.device_info.devices[n].id
|
|
dun = result.device_info.devices[n].unit
|
|
dtype = proto.PbDeviceType.Name(result.device_info.devices[n].type)
|
|
dstat = result.device_info.devices[n].status
|
|
dprop = result.device_info.devices[n].properties
|
|
|
|
# Building the status string
|
|
# TODO: This formatting should probably be moved elsewhere
|
|
dstat_msg = []
|
|
if dprop.read_only == True:
|
|
dstat_msg.append("Read-Only")
|
|
if dstat.protected == True and dprop.protectable == True:
|
|
dstat_msg.append("Write-Protected")
|
|
if dstat.removed == True and dprop.removable == True:
|
|
dstat_msg.append("No Media")
|
|
if dstat.locked == True and dprop.lockable == True:
|
|
dstat_msg.append("Locked")
|
|
|
|
dpath = result.device_info.devices[n].file.name
|
|
dfile = path.basename(dpath)
|
|
dparam = result.device_info.devices[n].params
|
|
dven = result.device_info.devices[n].vendor
|
|
dprod = result.device_info.devices[n].product
|
|
drev = result.device_info.devices[n].revision
|
|
dblock = result.device_info.devices[n].block_size
|
|
|
|
device_list.append({"id": did, "un": dun, "device_type": dtype, \
|
|
"status": ", ".join(dstat_msg), "image": dpath, "file": dfile, "params": dparam,\
|
|
"vendor": dven, "product": dprod, "revision": drev, "block_size": dblock})
|
|
occupied_ids.append(did)
|
|
n += 1
|
|
return device_list, occupied_ids
|
|
|
|
|
|
def sort_and_format_devices(device_list, occupied_ids):
|
|
# Add padding devices and sort the list
|
|
for id in range(8):
|
|
if id not in occupied_ids:
|
|
device_list.append({"id": id, "type": "-", \
|
|
"status": "-", "file": "-", "product": "-"})
|
|
# Sort list of devices by id
|
|
device_list.sort(key=lambda dic: str(dic["id"]))
|
|
|
|
return device_list
|
|
|
|
|
|
def reserve_scsi_ids(reserved_scsi_ids):
|
|
command = proto.PbCommand()
|
|
command.operation = proto.PbOperation.RESERVE
|
|
command.params.append(reserved_scsi_ids)
|
|
|
|
data = send_pb_command(command.SerializeToString())
|
|
result = proto.PbResult()
|
|
result.ParseFromString(data)
|
|
return {"status": result.status, "msg": result.msg}
|
|
|
|
|
|
def set_log_level(log_level):
|
|
'''Sends a command to the server to change the log level. Takes target log level as an argument'''
|
|
command = proto.PbCommand()
|
|
command.operation = proto.PbOperation.LOG_LEVEL
|
|
command.params.append(str(log_level))
|
|
|
|
data = send_pb_command(command.SerializeToString())
|
|
result = proto.PbResult()
|
|
result.ParseFromString(data)
|
|
return {"status": result.status, "msg": result.msg}
|
|
|
|
|
|
def send_pb_command(payload):
|
|
# Host and port number where rascsi is listening for socket connections
|
|
HOST = 'localhost'
|
|
PORT = 6868
|
|
|
|
counter = 0
|
|
tries = 100
|
|
error_msg = ""
|
|
|
|
import socket
|
|
while counter < tries:
|
|
try:
|
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
s.connect((HOST, PORT))
|
|
return send_over_socket(s, payload)
|
|
except socket.error as error:
|
|
counter += 1
|
|
logging.warning("The RaSCSI service is not responding - attempt " + \
|
|
str(counter) + "/" + str(tries))
|
|
error_msg = str(error)
|
|
|
|
logging.error(error_msg)
|
|
|
|
# After failing all attempts, throw a 404 error
|
|
from flask import abort
|
|
abort(404, "Failed to connect to RaSCSI at " + str(HOST) + ":" + str(PORT) + \
|
|
" with error: " + error_msg + ". Is the RaSCSI service running?")
|
|
|
|
|
|
def send_over_socket(s, payload):
|
|
from struct import pack, unpack
|
|
|
|
# Prepending a little endian 32bit header with the message size
|
|
s.send(pack("<i", len(payload)))
|
|
s.send(payload)
|
|
|
|
# Receive the first 4 bytes to get the response header
|
|
response = s.recv(4)
|
|
if len(response) >= 4:
|
|
# Extracting the response header to get the length of the response message
|
|
response_length = unpack("<i", response)[0]
|
|
# Reading in chunks, to handle a case where the response message is very large
|
|
chunks = []
|
|
bytes_recvd = 0
|
|
while bytes_recvd < response_length:
|
|
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.")
|
|
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.")
|