Merge pull request #286 from akuker/webui_cleanup

Webui cleanup
This commit is contained in:
Eric Helgeson 2021-09-29 19:58:15 -05:00 committed by GitHub
commit 998cc2d66f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 136 additions and 8 deletions

View File

@ -37,6 +37,11 @@ def list_config_files():
def create_new_image(file_name, file_type, size):
"""
Takes str file_name, str file_type, and int size
Sends a CREATE_IMAGE command to the server
Returns dict with boolean status and str msg
"""
command = proto.PbCommand()
command.operation = proto.PbOperation.CREATE_IMAGE
@ -51,6 +56,11 @@ def create_new_image(file_name, file_type, size):
def delete_file(file_name):
"""
Takes str file_name
Sends a DELETE_IMAGE command to the server
Returns dict with boolean status and str msg
"""
command = proto.PbCommand()
command.operation = proto.PbOperation.DELETE_IMAGE
@ -63,6 +73,10 @@ def delete_file(file_name):
def unzip_file(file_name):
"""
Takes str file_name
Returns dict with boolean status and str msg
"""
from subprocess import run
unzip_proc = run(
@ -76,6 +90,10 @@ def unzip_file(file_name):
def download_file_to_iso(scsi_id, url):
"""
Takes int scsi_id and str url
Returns dict with boolean status and str msg
"""
import urllib.request
import urllib.error as error
import time
@ -102,10 +120,16 @@ def download_file_to_iso(scsi_id, url):
)
if iso_proc.returncode != 0:
return {"status": False, "msg": iso_proc}
return attach_image(scsi_id, type="SCCD", image=iso_filename)
process = attach_image(scsi_id, type="SCCD", image=iso_filename)
return {"status": process["status"], "msg": process["msg"]}
def download_image(url):
"""
Takes str url
Returns dict with boolean status and str msg
"""
import urllib.request
import urllib.error as error
@ -123,6 +147,10 @@ def download_image(url):
def write_config(file_name):
"""
Takes str file_name
Returns dict with boolean status and str msg
"""
from json import dump
file_name = base_dir + file_name
try:
@ -154,6 +182,10 @@ def write_config(file_name):
def read_config(file_name):
"""
Takes str file_name
Returns dict with boolean status and str msg
"""
from json import load
file_name = base_dir + file_name
try:
@ -185,6 +217,7 @@ def write_drive_properties(file_name, conf):
"""
Writes a drive property configuration file to the images dir.
Takes file name base (str) and conf (list of dicts) as arguments
Returns dict with boolean status and str msg
"""
from json import dump
try:
@ -205,6 +238,7 @@ def read_drive_properties(path_name):
Reads drive properties to any dir.
Either ones deployed to the images dir, or the canonical database.
Takes file path and bas (str) as argument
Returns dict with boolean status and str msg
"""
from json import load
try:

View File

@ -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 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 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 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 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 boolean 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 boolean 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.")