Support for multiple SCSI LUNs (#318)

* Updated logging

* Updated logging

* Updated logging

* Updated ID/LUN parsing

* Updated handling of max_id

* The -HD option sets type to SAHD

* Replaced is_sasi by device type

* Updated logging

* Logging update

* Improved LUN evaluation

* Check LUN against UnitMax

* Comment update

* LUN parsing update

* Logging update

* Logging update

* Updated ReportLuns

* Updated REPORT LUNS

* Cleanup

* Updated REPORT LUNS

* Updated Execute()

* Updated LUN handling

* Check for consecutive LUNs

* Added LUN check for remotely attached devices

* Remember LUN selected by IDENTIFY

* Evaluate LUN from IDENTIFY message

* Added comment

* Updated REPORT LUNS

* Initlize LUN

* Logging update

* Support 32 LUNs

* rasctl display update

* Updated LUN check for LUNSs > 7

* Simplified LUN validation

* Fixed wrong ID/LUN handling with values > 9

* Log level update

* Manpage update

* rascsi parser update

* Updated error handling

* Updated error handling

* Updated LUN setup validation

* Updated logging

* Improved validation of consecutive LUNs

* Renaming

* Detach all LUNs equal to or higher than the one specified

* Add support for LUN in the device list

* Add ability to show device info for LUNs

* Make it possible to detach and eject LUNs

* Show full path to prop file

* Show only LUN columns when non-0 LUNs present

* Support for attaching LUNs

* Add helptext

* Fix handling of removable media

* Retain the previous behavior of recommending the next unoccupied id

* SCSI ID validation no longer needed due to changed logic

* Make use of recommended id everywhere

* Docstring

Co-authored-by: Daniel Markstedt <markstedt@gmail.com>
This commit is contained in:
Uwe Seimet 2021-10-13 11:03:31 +02:00 committed by GitHub
parent be090de0cc
commit 89d66ef02b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 372 additions and 221 deletions

View File

@ -11,15 +11,15 @@ rascsi \- Emulates SCSI devices using the Raspberry Pi GPIO pins
[\fB\-r\fR \fIRESERVED_IDS\fR]
[\fB\-n\fR \fITYPE\fR]
[\fB\-v\fR]
[\fB\-IDn\fR \fIFILE\fR]
[\fB\-HDn\fR \fIFILE\fR]...
[\fB\-IDn:[u]\fR \fIFILE\fR]
[\fB\-HDn[:u]\fR \fIFILE\fR]...
.SH DESCRIPTION
.B rascsi
Emulates SCSI devices using the Raspberry Pi GPIO pins.
.PP
In the arguments to RaSCSI, one or more SCSI (-IDn) or SASI (-HDn) devices can be specified.
The number (n) after the ID or HD identifier specifies the ID number for that device.
For SCSI: The ID is limited from 0-7. However, typically SCSI ID 7 is reserved for the "initiator" (the host computer). Note that SASI is considered rare and only used on very early Sharp X68000 computers.
In the arguments to RaSCSI, one or more SCSI (-IDn[:u]) or SASI (-HDn[:u]) devices can be specified.
The number (n) after the ID or HD identifier specifies the ID number for that device. The optional number (u) specifies the LUN (logical unit) for that device. The default LUN is 0.
For SCSI: The ID is limited from 0-7. However, typically SCSI ID 7 is reserved for the "initiator" (the host computer). The LUN is limited from 0-31. Note that SASI is considered rare and only used on very early Sharp X68000 computers.
.PP
RaSCSI will determine the type of device based upon the file extension of the FILE argument.
hdf: SASI Hard Disk image (XM6 SASI HD image - typically only used with X68000)
@ -69,13 +69,13 @@ The optional case-insensitive device type (SAHD, SCHD, SCRM, SCCD, SCMO, SCBR, S
.BR \-v\fI " " \fI
Display the rascsi version.
.TP
.BR \-ID\fIn " " \fIFILE
n is the SCSI ID number (0-7)
.BR \-ID\fIn[:u] " " \fIFILE
n is the SCSI ID number (0-7). u (0-31) is the optional LUN (logical unit). The default LUN is 0.
.IP
FILE is the name of the image file to use for the SCSI device. For devices that do not support an image file (SCBR, SCDP) a dummy name must be provided.
.TP
.BR \-HD\fIn " " \fIFILE
n is the SASI ID number (0-15)
.BR \-HD\fIn[:u] " " \fIFILE
n is the SASI ID number (0-15). The effective SASI ID is calculated as n/2, the effective SASI LUN is calculated is the remainder of n/2. Alternatively the n:u syntax can be used, where ns is the SASI ID (0-7) and u the LUN (0-1).
.IP
FILE is the name of the image file to use for the SASI device.
.IP

View File

@ -7,18 +7,20 @@ NAME
SYNOPSIS
rascsi [-F[u00AE] FOLDER] [-L[u00AE] LOG_LEVEL] [-h] [-n VENDOR:PROD
UCT:REVISION] [-p[u00AE] PORT] [-r RESERVED_IDS] [-n TYPE] [-v] [-IDn
FILE] [-HDn FILE]...
UCT:REVISION] [-p[u00AE] PORT] [-r RESERVED_IDS] [-n TYPE] [-v]
[-IDn:[u] FILE] [-HDn[:u] FILE]...
DESCRIPTION
rascsi Emulates SCSI devices using the Raspberry Pi GPIO pins.
In the arguments to RaSCSI, one or more SCSI (-IDn) or SASI (-HDn) de
vices can be specified. The number (n) after the ID or HD identifier
specifies the ID number for that device. For SCSI: The ID is limited
from 0-7. However, typically SCSI ID 7 is reserved for the "initiator"
(the host computer). Note that SASI is considered rare and only used on
very early Sharp X68000 computers.
In the arguments to RaSCSI, one or more SCSI (-IDn[:u]) or SASI
(-HDn[:u]) devices can be specified. The number (n) after the ID or HD
identifier specifies the ID number for that device. The optional number
(u) specifies the LUN (logical unit) for that device. The default LUN
is 0. For SCSI: The ID is limited from 0-7. However, typically SCSI ID
7 is reserved for the "initiator" (the host computer). The LUN is lim
ited from 0-31. Note that SASI is considered rare and only used on very
early Sharp X68000 computers.
RaSCSI will determine the type of device based upon the file extension
of the FILE argument.
@ -85,15 +87,19 @@ OPTIONS
-v Display the rascsi version.
-IDn FILE
n is the SCSI ID number (0-7)
-IDn[:u] FILE
n is the SCSI ID number (0-7). u (0-31) is the optional LUN
(logical unit). The default LUN is 0.
FILE is the name of the image file to use for the SCSI device.
For devices that do not support an image file (SCBR, SCDP) a
FILE is the name of the image file to use for the SCSI device.
For devices that do not support an image file (SCBR, SCDP) a
dummy name must be provided.
-HDn FILE
n is the SASI ID number (0-15)
-HDn[:u] FILE
n is the SASI ID number (0-15). The effective SASI ID is calcu
lated as n/2, the effective SASI LUN is calculated is the re
mainder of n/2. Alternatively the n:u syntax can be used, where
ns is the SASI ID (0-7) and u the LUN (0-1).
FILE is the name of the image file to use for the SASI device.
@ -121,7 +127,7 @@ EXAMPLES
fallocate -l 104857600 /path/to/newimage.hda
SEE ALSO
rasctl(1), scsimon(1)
rasctl(1), scsimon(1), rasdump(1), sasidump(1)
Full documentation is available at:
<https://www.github.com/akuker/RASCSI/wiki/>

View File

@ -134,7 +134,7 @@ Specifies the device type. This type overrides the type derived from the file ex
The vendor, product and revision for the device, to be returned with the INQUIRY data. A complete set of name components must be provided. VENDOR may have up to 8, PRODUCT up to 16, REVISION up to 4 characters. Padding with blanks to the maxium length is automatically applied. Once set the name of a device cannot be changed.
.TP
.BR \-u\fI " " \fIUNIT
Unit number (0 or 1). This will default to 0. This option is only used when there are multiple SCSI devices on a shared SCSI controller. (This is not common)
Unit number (0-31). This will default to 0. This option is only used when there are multiple SCSI devices on a shared SCSI controller. (This is not common)
.SH EXAMPLES
Show a listing of all of the SCSI devices and their current status.

View File

@ -127,9 +127,9 @@ OPTIONS
changed.
-u UNIT
Unit number (0 or 1). This will default to 0. This option is
only used when there are multiple SCSI devices on a shared SCSI
controller. (This is not common)
Unit number (0-31). This will default to 0. This option is only
used when there are multiple SCSI devices on a shared SCSI con
troller. (This is not common)
EXAMPLES
Show a listing of all of the SCSI devices and their current status.

View File

@ -50,6 +50,7 @@ SASIDEV::SASIDEV()
ctrl.next = 0;
ctrl.offset = 0;
ctrl.length = 0;
ctrl.lun = -1;
// Logical unit initialization
for (int i = 0; i < UnitMax; i++) {
@ -235,6 +236,9 @@ void SASIDEV::BusFree()
// Initialize status and message
ctrl.status = 0x00;
ctrl.message = 0x00;
ctrl.lun = -1;
return;
}
@ -304,11 +308,9 @@ void SASIDEV::Command()
ctrl.length = 6;
ctrl.blocks = 1;
// Command reception handshake (10 bytes are automatically received at the first command)
int count = ctrl.bus->CommandHandShake(ctrl.buffer);
// If no byte can be received move to the status phase
if (count == 0) {
int count = ctrl.bus->CommandHandShake(ctrl.buffer);
if (!count) {
Error();
return;
}
@ -324,6 +326,7 @@ void SASIDEV::Command()
// Command data transfer
for (int i = 0; i < (int)ctrl.length; i++) {
ctrl.cmd[i] = (DWORD)ctrl.buffer[i];
LOGTRACE("%s Command Byte %d: $%02X",__PRETTY_FUNCTION__, i, ctrl.cmd[i]);
}
// Clear length and block
@ -424,10 +427,10 @@ void SASIDEV::Execute()
}
// Unsupported command
LOGWARN("%s ID %d received unsupported command: $%02X", __PRETTY_FUNCTION__, GetSCSIID(), (BYTE)ctrl.cmd[0]);
LOGTRACE("%s ID %d received unsupported command: $%02X", __PRETTY_FUNCTION__, GetSCSIID(), (BYTE)ctrl.cmd[0]);
// Logical Unit
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
DWORD lun = GetEffectiveLun();
if (ctrl.unit[lun]) {
// Command processing on drive
ctrl.unit[lun]->SetStatusCode(STATUS_INVALIDCMD);
@ -637,7 +640,7 @@ void SASIDEV::Error(ERROR_CODES::sense_key sense_key, ERROR_CODES::asc asc)
}
// Logical Unit
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
DWORD lun = GetEffectiveLun();
// Set status and message(CHECK CONDITION)
ctrl.status = (lun << 5) | 0x02;
@ -1066,7 +1069,7 @@ bool SASIDEV::XferIn(BYTE *buf)
LOGTRACE("%s ctrl.cmd[0]=%02X", __PRETTY_FUNCTION__, (unsigned int)ctrl.cmd[0]);
// Logical Unit
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
DWORD lun = GetEffectiveLun();
if (!ctrl.unit[lun]) {
return false;
}
@ -1111,7 +1114,7 @@ bool SASIDEV::XferOut(bool cont)
ASSERT(ctrl.phase == BUS::dataout);
// Logical Unit
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
DWORD lun = GetEffectiveLun();
if (!ctrl.unit[lun]) {
return false;
}
@ -1208,7 +1211,7 @@ void SASIDEV::FlushUnit()
ASSERT(ctrl.phase == BUS::dataout);
// Logical Unit
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
DWORD lun = GetEffectiveLun();
if (!ctrl.unit[lun]) {
return;
}
@ -1259,3 +1262,9 @@ void SASIDEV::FlushUnit()
break;
}
}
int SASIDEV::GetEffectiveLun() const
{
return ctrl.lun != -1 ? ctrl.lun : (ctrl.cmd[1] >> 5) & 0x07;
}

View File

@ -83,7 +83,7 @@ private:
public:
enum {
UnitMax = 8 // Maximum number of logical units
UnitMax = 32 // Maximum number of logical units
};
const int UNKNOWN_SCSI_ID = -1;
@ -123,7 +123,11 @@ public:
// Logical unit
Disk *unit[UnitMax];
// The current device
Disk *device;
// The LUN from the IDENTIFY message
int lun;
} ctrl_t;
public:
@ -155,6 +159,8 @@ public:
void MsgIn(); // Message in phase
void DataOut(); // Data out phase
int GetEffectiveLun() const;
virtual void Error(ERROR_CODES::sense_key sense_key = ERROR_CODES::sense_key::NO_SENSE,
ERROR_CODES::asc = ERROR_CODES::asc::NO_ADDITIONAL_SENSE_INFORMATION); // Common error handling
@ -189,6 +195,5 @@ protected:
// Special operations
void FlushUnit(); // Flush the logical unit
protected:
ctrl_t ctrl; // Internal data
};

View File

@ -166,6 +166,9 @@ void SCSIDEV::BusFree()
// Initialize ATN message reception status
scsi.atnmsg = false;
ctrl.lun = -1;
return;
}
@ -240,33 +243,38 @@ void SCSIDEV::Execute()
LOGDEBUG("++++ CMD ++++ %s Executing command $%02X", __PRETTY_FUNCTION__, (unsigned int)ctrl.cmd[0]);
// Use LUN0 for INQUIRY and REQUEST SENSE because LUN0 is assumed to be always available.
// INQUIRY and REQUEST SENSE have a special LUN handling of their own, required by the SCSI standard.
int lun = 0;
if ((SCSIDEV::scsi_command)ctrl.cmd[0] != eCmdInquiry && (SCSIDEV::scsi_command)ctrl.cmd[0] != eCmdRequestSense) {
lun = (ctrl.cmd[1] >> 5) & 0x07;
if (!ctrl.unit[lun]) {
int lun = GetEffectiveLun();
if (!ctrl.unit[lun]) {
if ((SCSIDEV::scsi_command)ctrl.cmd[0] != eCmdInquiry && (SCSIDEV::scsi_command)ctrl.cmd[0] != eCmdRequestSense) {
LOGDEBUG("Invalid LUN %d for ID %d", lun, GetSCSIID());
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_LUN);
return;
}
// Use LUN 0 for INQUIRY and REQUEST SENSE because LUN0 is assumed to be always available.
// INQUIRY and REQUEST SENSE have a special LUN handling of their own, required by the SCSI standard.
else {
assert(ctrl.unit[0]);
lun = 0;
}
}
ctrl.device = ctrl.unit[lun];
if (!ctrl.device->Dispatch(this)) {
LOGWARN("ID %d LUN %d received unsupported command: $%02X", GetSCSIID(), lun, (BYTE)ctrl.cmd[0]);
LOGTRACE("ID %d LUN %d received unsupported command: $%02X", GetSCSIID(), lun, (BYTE)ctrl.cmd[0]);
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_COMMAND_OPERATION_CODE);
}
if ((SCSIDEV::scsi_command)ctrl.cmd[0] == eCmdInquiry) {
// SCSI-2 p.104 4.4.3 Incorrect logical unit handling
if (((ctrl.cmd[1] >> 5) & 0x07) != (DWORD)ctrl.device->GetLun()) {
ctrl.buffer[0] = 0x7f;
}
// SCSI-2 p.104 4.4.3 Incorrect logical unit handling
if ((SCSIDEV::scsi_command)ctrl.cmd[0] == eCmdInquiry && !ctrl.unit[lun]) {
lun = GetEffectiveLun();
LOGDEBUG("Reporting LUN %d for device ID %d as not supported", lun, ctrl.device->GetId());
ctrl.buffer[0] = 0x7f;
}
}
@ -559,7 +567,7 @@ void SCSIDEV::Receive()
for (int i = 0; i < len; i++) {
ctrl.cmd[i] = (DWORD)ctrl.buffer[i];
LOGTRACE("%s Command $%02X",__PRETTY_FUNCTION__, ctrl.cmd[i]);
LOGTRACE("%s Command Byte %d: $%02X",__PRETTY_FUNCTION__, i, ctrl.cmd[i]);
}
// Execution Phase
@ -601,7 +609,8 @@ void SCSIDEV::Receive()
// IDENTIFY
if (data >= 0x80) {
LOGTRACE("Message code IDENTIFY $%02X", (int)data);
ctrl.lun = data & 0x1F;
LOGTRACE("Message code IDENTIFY $%02X, LUN %d selected", data, ctrl.lun);
}
// Extended Message
@ -687,4 +696,3 @@ bool SCSIDEV::XferMsg(DWORD msg)
return true;
}

View File

@ -146,7 +146,7 @@ Device *DeviceFactory::CreateDevice(PbDeviceType type, const string& filename)
device->SetProduct("FIREBALL");
}
}
device->SetSupportedLuns(1);
device->SetSupportedLuns(32);
device->SetProtectable(true);
device->SetStoppable(true);
break;
@ -154,7 +154,7 @@ Device *DeviceFactory::CreateDevice(PbDeviceType type, const string& filename)
case SCRM:
device = new SCSIHD(true);
device->SetSupportedLuns(1);
device->SetSupportedLuns(32);
device->SetProtectable(true);
device->SetStoppable(true);
device->SetRemovable(true);
@ -165,7 +165,7 @@ Device *DeviceFactory::CreateDevice(PbDeviceType type, const string& filename)
case SCMO:
device = new SCSIMO();
device->SetSupportedLuns(1);
device->SetSupportedLuns(32);
device->SetProtectable(true);
device->SetStoppable(true);
device->SetRemovable(true);
@ -177,7 +177,7 @@ Device *DeviceFactory::CreateDevice(PbDeviceType type, const string& filename)
case SCCD:
device = new SCSICD();
device->SetSupportedLuns(1);
device->SetSupportedLuns(32);
device->SetReadOnly(true);
device->SetStoppable(true);
device->SetRemovable(true);

View File

@ -166,7 +166,7 @@ void Disk::Rezero(SASIDEV *controller)
void Disk::RequestSense(SASIDEV *controller)
{
int lun = (ctrl->cmd[1] >> 5) & 0x07;
int lun = controller->GetEffectiveLun();
// Note: According to the SCSI specs the LUN handling for REQUEST SENSE non-existing LUNs do *not* result
// in CHECK CONDITION. Only the Sense Key and ASC are set in order to signal the non-existing LUN.
@ -366,14 +366,20 @@ void Disk::Verify16(SASIDEV *controller)
void Disk::Inquiry(SASIDEV *controller)
{
ScsiPrimaryCommands *device = NULL;
int lun = controller->GetEffectiveLun();
device = ctrl->unit[lun];
// Find a valid unit
// TODO The code below is probably wrong. It results in the same INQUIRY data being
// used for all LUNs, even though each LUN has its individual set of INQUIRY data.
ScsiPrimaryCommands *device = NULL;
for (int valid_lun = 0; valid_lun < SASIDEV::UnitMax; valid_lun++) {
if (ctrl->unit[valid_lun]) {
device = ctrl->unit[valid_lun];
break;
// In addition, it supports gaps in the LUN list, which is not correct.
if (!device) {
for (int valid_lun = 0; valid_lun < SASIDEV::UnitMax; valid_lun++) {
if (ctrl->unit[valid_lun]) {
device = ctrl->unit[valid_lun];
break;
}
}
}
@ -389,8 +395,9 @@ void Disk::Inquiry(SASIDEV *controller)
}
// Report if the device does not support the requested LUN
int lun = (ctrl->cmd[1] >> 5) & 0x07;
if (!ctrl->unit[lun]) {
LOGDEBUG("Reporting LUN %d for device ID %d as not supported", lun, ctrl->device->GetId());
ctrl->buffer[0] |= 0x7f;
}
@ -1456,19 +1463,30 @@ void Disk::ReportLuns(SASIDEV *controller)
ASSERT(buf);
memset(buf, 0, 16);
if (!CheckReady()) {
controller->Error();
return;
}
// LUN list length
buf[3] = 8;
int allocation_length = (ctrl->cmd[6] << 24) + (ctrl->cmd[7] << 16) + (ctrl->cmd[8] << 8) + ctrl->cmd[9];
memset(buf, 0, allocation_length);
// As long as there is no proper support for more than one SCSI LUN no other fields must be set => 1 LUN
// Count number of available LUNs for the current device
int luns;
for (luns = 0; luns < controller->GetCtrl()->device->GetSupportedLuns(); luns++) {
if (!controller->GetCtrl()->unit[luns]) {
break;
}
}
ctrl->length = 16;
// LUN list length, 8 bytes per LUN
// SCSI standard: The contents of the LUN LIST LENGTH field are not altered based on the allocation length
buf[0] = (luns * 8) >> 24;
buf[1] = (luns * 8) >> 16;
buf[2] = (luns * 8) >> 8;
buf[3] = luns * 8;
ctrl->length = allocation_length < 8 + luns * 8 ? allocation_length : 8 + luns * 8;
controller->DataIn();
}

View File

@ -52,7 +52,7 @@ using namespace rascsi_interface;
//
//---------------------------------------------------------------------------
#define CtrlMax 8 // Maximum number of SCSI controllers
#define UnitNum 2 // Number of units around controller
#define UnitNum SASIDEV::UnitMax // Number of units around controller
#define FPRT(fp, ...) fprintf(fp, __VA_ARGS__ )
//---------------------------------------------------------------------------
@ -375,6 +375,48 @@ bool MapController(Device **map)
return status;
}
string ValidateLunSetup(const PbCommand& command, const vector<Device *>& existing_devices)
{
// Mapping of available LUNs (bit vector) to devices
map<uint32_t, uint32_t> luns;
// Collect LUN vectors of new devices
for (const auto& device : command.devices()) {
luns[device.id()] |= 1 << device.unit();
}
// Collect LUN vectors of existing devices
for (auto const& device : existing_devices) {
if (device) {
luns[device->GetId()] |= 1 << device->GetLun();
}
}
// LUNs must be consecutive
for (auto const& [id, lun]: luns) {
bool is_consecutive = false;
uint32_t lun_vector = 0;
for (int i = 0; i < 32; i++) {
lun_vector |= 1 << i;
if (lun == lun_vector) {
is_consecutive = true;
break;
}
}
if (!is_consecutive) {
ostringstream error;
error << "LUNs for device ID " << id << " are not consecutive";
return error.str();
}
}
return "";
}
bool ReturnStatus(int fd, bool status = true, const string msg = "")
{
if (!status && !msg.empty()) {
@ -451,7 +493,7 @@ void LogDevices(const string& devices)
}
}
const char *SetDefaultImageFolder(const string& f)
string SetDefaultImageFolder(const string& f)
{
string folder = f;
@ -479,14 +521,14 @@ const char *SetDefaultImageFolder(const string& f)
struct stat info;
stat(folder.c_str(), &info);
if (!S_ISDIR(info.st_mode) || access(folder.c_str(), F_OK) == -1) {
return string("Folder '" + f + "' does not exist or is not accessible").c_str();
return string("Folder '" + f + "' does not exist or is not accessible");
}
default_image_folder = folder;
LOGINFO("Default image folder set to '%s'", default_image_folder.c_str());
return NULL;
return "";
}
string SetReservedIds(const string& ids)
@ -833,6 +875,20 @@ bool Attach(int fd, const PbDeviceDefinition& pb_device, Device *map[], bool dry
}
}
int supported_luns = device->GetSupportedLuns();
if (unit >= supported_luns) {
delete device;
error << "Invalid unit " << unit << " for device type " << PbDeviceType_Name(type);
if (supported_luns == 1) {
error << " (0)";
}
else {
error << " (0-" << (supported_luns -1) << ")";
}
return ReturnStatus(fd, false, error.str());
}
// If no filename was provided the medium is considered removed
FileSupport *file_support = dynamic_cast<FileSupport *>(device);
if (file_support) {
@ -864,11 +920,15 @@ bool Attach(int fd, const PbDeviceDefinition& pb_device, Device *map[], bool dry
Disk *disk = dynamic_cast<Disk *>(device);
if (disk && disk->IsSectorSizeConfigurable()) {
if (!disk->SetConfiguredSectorSize(pb_device.block_size())) {
delete device;
error << "Invalid block size " << pb_device.block_size() << " bytes";
return ReturnStatus(fd, false, error);
}
}
else {
delete device;
return ReturnStatus(fd, false, "Block size is not configurable for device type " + PbDeviceType_Name(type));
}
}
@ -957,30 +1017,26 @@ bool Attach(int fd, const PbDeviceDefinition& pb_device, Device *map[], bool dry
return ReturnStatus(fd, false, "SASI and SCSI can't be mixed");
}
bool Detach(int fd, const PbDeviceDefinition& pb_device, Device *map[], bool dryRun)
bool Detach(int fd, Device *device, Device *map[], bool dryRun)
{
if (!dryRun) {
const int id = pb_device.id();
const int unit = pb_device.unit();
for (size_t i = devices.size() - 1; i > 0; i--) {
Device *d = map[i];
// Detach all LUNs equal to or higher than the LUN specified
if (d && d->GetId() == device->GetId() && d->GetLun() >= device->GetLun()) {
map[d->GetId() * UnitNum + d->GetLun()] = NULL;
Device *device = map[id * UnitNum + unit];
FileSupport *file_support = dynamic_cast<FileSupport *>(d);
if (file_support) {
file_support->UnreserveFile();
}
map[id * UnitNum + unit] = NULL;
LOGINFO("Detached %s device with ID %d, unit %d", d->GetType().c_str(), d->GetId(), d->GetLun());
}
FileSupport *file_support = dynamic_cast<FileSupport *>(device);
if (file_support) {
file_support->UnreserveFile();
// Re-map the controller
MapController(map);
}
// Re-map the controller, remember the device type because the device gets lost when re-mapping
const string device_type = device ? device->GetType() : PbDeviceType_Name(UNDEFINED);
bool status = MapController(map);
if (status) {
LOGINFO("Detached %s device with ID %d, unit %d", device_type.c_str(), id, unit);
return true;
}
return ReturnStatus(fd, false, "SASI and SCSI can't be mixed");
}
return true;
@ -1145,12 +1201,12 @@ bool ProcessCmd(int fd, const PbDeviceDefinition& pb_device, const PbCommand& co
// Does the unit exist?
Device *device = devices[id * UnitNum + unit];
if (!device) {
error << "Received a command for a non-existing device, ID " << id << ", unit " << unit;
error << "Received a command for a non-existing device or unit, ID " << id << ", unit " << unit;
return ReturnStatus(fd, false, error);
}
if (operation == DETACH) {
return Detach(fd, pb_device, map, dryRun);
return Detach(fd, device, map, dryRun);
}
if ((operation == START || operation == STOP) && !device->IsStoppable()) {
@ -1280,8 +1336,14 @@ bool ProcessCmd(const int fd, const PbCommand& command)
}
}
// Restore list of reserved files, then execute the command
// Restore the list of reserved files before proceeding
FileSupport::SetReservedFiles(reserved_files);
string result = ValidateLunSetup(command, devices);
if (!result.empty()) {
return ReturnStatus(fd, false, result);
}
for (const auto& device : command.devices()) {
if (!ProcessCmd(fd, device, command, false)) {
return false;
@ -1291,6 +1353,37 @@ bool ProcessCmd(const int fd, const PbCommand& command)
return ReturnStatus(fd);
}
bool ProcessId(const string id_spec, PbDeviceType type, int& id, int& unit)
{
size_t separator_pos = id_spec.find(':');
if (separator_pos == string::npos) {
int max_id = type == SAHD ? 16 : 8;
if (!GetAsInt(id_spec, id) || id < 0 || id >= max_id) {
cerr << optarg << ": Invalid device ID (0-" << (max_id - 1) << ")" << endl;
return false;
}
// Required for SASI ID/LUN handling backwards compatibility
unit = 0;
if (type == SAHD) {
unit = id % 2;
id /= 2;
}
}
else {
int max_unit = type == SAHD ? 2 : UnitNum;
if (!GetAsInt(id_spec.substr(0, separator_pos), id) || id < 0 || id > 7 ||
!GetAsInt(id_spec.substr(separator_pos + 1), unit) || unit < 0 || unit >= max_unit) {
cerr << optarg << ": Invalid unit (0-" << (max_unit - 1) << ")" << endl;
return false;
}
}
return true;
}
//---------------------------------------------------------------------------
//
// Argument Parsing
@ -1300,8 +1393,7 @@ bool ParseArgument(int argc, char* argv[], int& port)
{
PbCommand command;
int id = -1;
bool is_sasi = false;
int max_id = 7;
int unit = -1;
PbDeviceType type = UNDEFINED;
int block_size = 0;
string name;
@ -1314,24 +1406,20 @@ bool ParseArgument(int argc, char* argv[], int& port)
// The three options below are kind of a compound option with two letters
case 'i':
case 'I':
is_sasi = false;
max_id = 7;
id = -1;
unit = -1;
continue;
case 'h':
case 'H':
is_sasi = true;
max_id = 15;
id = -1;
unit = -1;
type = SAHD;
continue;
case 'd':
case 'D': {
char* end;
id = strtol(optarg, &end, 10);
if (*end || id < 0 || max_id < id) {
cerr << optarg << ": invalid " << (is_sasi ? "HD" : "ID") << " (0-" << max_id << ")" << endl;
if (!ProcessId(optarg, type, id, unit)) {
return false;
}
continue;
@ -1346,8 +1434,8 @@ bool ParseArgument(int argc, char* argv[], int& port)
}
case 'F': {
const char *result = SetDefaultImageFolder(optarg);
if (result) {
string result = SetDefaultImageFolder(optarg);
if (!result.empty()) {
cerr << result << endl;
return false;
}
@ -1400,12 +1488,6 @@ bool ParseArgument(int argc, char* argv[], int& port)
return false;
}
int unit = 0;
if (is_sasi) {
unit = id % UnitNum;
id /= UnitNum;
}
// Set up the device data
PbDeviceDefinition *device = command.add_devices();
device->set_id(id);
@ -1414,14 +1496,14 @@ bool ParseArgument(int argc, char* argv[], int& port)
device->set_block_size(block_size);
AddParam(*device, "file", optarg);
size_t separatorPos = name.find(':');
if (separatorPos != string::npos) {
device->set_vendor(name.substr(0, separatorPos));
name = name.substr(separatorPos + 1);
separatorPos = name.find(':');
if (separatorPos != string::npos) {
device->set_product(name.substr(0, separatorPos));
device->set_revision(name.substr(separatorPos + 1));
size_t separator_pos = name.find(':');
if (separator_pos != string::npos) {
device->set_vendor(name.substr(0, separator_pos));
name = name.substr(separator_pos + 1);
separator_pos = name.find(':');
if (separator_pos != string::npos) {
device->set_product(name.substr(0, separator_pos));
device->set_revision(name.substr(separator_pos + 1));
}
else {
device->set_product(name);
@ -1543,8 +1625,8 @@ static void *MonThread(void *param)
ReturnStatus(fd, false, "Can't set default image folder: Missing folder name");
}
const char *result = SetDefaultImageFolder(folder);
if (result) {
string result = SetDefaultImageFolder(folder);
if (!result.empty()) {
ReturnStatus(fd, false, result);
}
else {

View File

@ -37,7 +37,7 @@ enum PbOperation {
// "interfaces": A prioritized comma-separated list of interfaces to create a network bridge for.
ATTACH = 1;
// Detach devices
// Detach a device. Detaches all LUNs of that device which are equal to or higher than the LUN specified.
DETACH = 2;
// Detach all devices, does not require a device list
@ -171,7 +171,7 @@ message PbDeviceProperties {
bool supports_params = 7;
// List of default parameters, if any (requires supports_params to be true)
map<string, string> default_params = 8;
// Number of supported LUNs, at least 1 (for LUN 0)
// LUN numbers this device can represent. At least 1 (for LUN 0 only). Maxium is 32, for LUNs 0-31.
int32 luns = 9;
// Unordered list of permitted block sizes in bytes, empty if the block size is not configurable
repeated uint32 block_sizes = 10;

View File

@ -101,7 +101,7 @@ int main(int argc, char* argv[])
cerr << "[-C FILENAME:FILESIZE] [-w FILENAME] [-R CURRENT_NAME:NEW_NAME] [-x CURRENT_NAME:NEW_NAME] ";
cerr << "[-e] [-E FILENAME] [-I] [-l] [-L] [-m] [-O] [-s] [-v] [-V] [-y]" << endl;
cerr << " where ID := {0-7}" << endl;
cerr << " UNIT := {0|1}, default is 0" << endl;
cerr << " UNIT := {0-31}, default is 0" << endl;
cerr << " CMD := {attach|detach|insert|eject|protect|unprotect|show}" << endl;
cerr << " TYPE := {sahd|schd|scrm|sccd|scmo|scbr|scdp} or convenience type {hd|rm|mo|cd|bridge|daynaport}" << endl;
cerr << " BLOCK_SIZE := {256|512|1024|2048|4096) bytes per hard disk drive block" << endl;
@ -137,13 +137,25 @@ int main(int argc, char* argv[])
int opt;
while ((opt = getopt(argc, argv, "elmsvINOTVa:b:c:f:h:i:n:p:r:t:u:x:C:D:E:F:L:R:")) != -1) {
switch (opt) {
case 'i':
device->set_id(optarg[0] - '0');
case 'i': {
int id;
if (!GetAsInt(optarg, id)) {
cerr << "Error: Invalid device ID " << optarg;
exit(EXIT_FAILURE);
}
device->set_id(id);
break;
}
case 'u':
device->set_unit(optarg[0] - '0');
case 'u': {
int unit;
if (!GetAsInt(optarg, unit)) {
cerr << "Error: Invalid unit " << optarg;
exit(EXIT_FAILURE);
}
device->set_unit(unit);
break;
}
case 'C':
command.set_operation(CREATE_IMAGE);
@ -238,14 +250,14 @@ int main(int argc, char* argv[])
string revision;
string s = optarg;
size_t separatorPos = s.find(COMPONENT_SEPARATOR);
if (separatorPos != string::npos) {
vendor = s.substr(0, separatorPos);
s = s.substr(separatorPos + 1);
separatorPos = s.find(COMPONENT_SEPARATOR);
if (separatorPos != string::npos) {
product = s.substr(0, separatorPos);
revision = s.substr(separatorPos + 1);
size_t separator_pos = s.find(COMPONENT_SEPARATOR);
if (separator_pos != string::npos) {
vendor = s.substr(0, separator_pos);
s = s.substr(separator_pos + 1);
separator_pos = s.find(COMPONENT_SEPARATOR);
if (separator_pos != string::npos) {
product = s.substr(0, separator_pos);
revision = s.substr(separator_pos + 1);
}
else {
product = s;

View File

@ -122,7 +122,11 @@ void RasctlDisplay::DisplayDeviceTypesInfo(const PbDeviceTypesInfo& device_types
const PbDeviceProperties& properties = it->properties();
cout << " Supported LUNs: " << properties.luns() << endl;
cout << " Supported LUN numbers: 0";
if (properties.luns() > 1) {
cout << "-" << (properties.luns() - 1);
}
cout << endl;
if (properties.read_only() || properties.protectable() || properties.stoppable() || properties.read_only()
|| properties.lockable()) {

View File

@ -97,45 +97,28 @@ def get_device_types():
return {"status": result.status, "device_types": 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."}
else:
return {"status": False, "msg": "Invalid SCSI ID. Should be a number between 0-7"}
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.
Returns:
- list of ints valid_ids, which are the SCSI ids that are not reserved
- int recommended_id, which is the id that the Web UI should default to recommend
"""
occupied_ids = []
for d in devices:
# Make it possible to insert images on top of a
# removable media device currently without an image attached
if d["device_type"] != "-" and "No Media" not in d["status"]:
occupied_ids.append(d["id"])
occupied_ids.append(d["id"])
# Combine lists and remove duplicates
invalid_ids = list(set(reserved_ids + occupied_ids))
valid_ids = list(range(8))
for id in invalid_ids:
try:
valid_ids.remove(int(id))
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.")
valid_ids.reverse()
return valid_ids
unoccupied_ids = [i for i in list(range(8)) if i not in reserved_ids + occupied_ids]
unoccupied_ids.sort()
valid_ids = [i for i in list(range(8)) if i not in reserved_ids]
valid_ids.sort(reverse=True)
if len(unoccupied_ids) > 0:
recommended_id = unoccupied_ids[-1]
else:
recommended_id = occupied_ids.pop(0)
return valid_ids, recommended_id
def attach_image(scsi_id, **kwargs):
@ -165,7 +148,7 @@ def attach_image(scsi_id, **kwargs):
# Handling the inserting of media into an attached removable type device
device_type = kwargs.get("device_type", None)
currently_attached = list_devices(scsi_id)["device_list"]
currently_attached = list_devices(scsi_id, kwargs.get("unit"))["device_list"]
if len(currently_attached) > 0:
current_type = currently_attached[0]["device_type"]
else:
@ -173,7 +156,8 @@ def attach_image(scsi_id, **kwargs):
if device_type in REMOVABLE_DEVICE_TYPES and current_type in REMOVABLE_DEVICE_TYPES:
if current_type != device_type:
return {"status": False, "msg": f"Cannot insert an image for {device_type} into a {current_type} device."}
return {"status": False, "msg": f"Cannot insert an image for \
{device_type} into a {current_type} device."}
else:
command.operation = proto.PbOperation.INSERT
# Handling attaching a new device
@ -203,13 +187,16 @@ def attach_image(scsi_id, **kwargs):
return {"status": result.status, "msg": result.msg}
def detach_by_id(scsi_id):
def detach_by_id(scsi_id, un=None):
"""
Takes int scsi_id and sends a DETACH command to the server.
Takes int scsi_id and optional int un
Sends a DETACH command to the server.
Returns boolean status and str msg.
"""
devices = proto.PbDeviceDefinition()
devices.id = int(scsi_id)
if un != None:
devices.unit = int(un)
command = proto.PbCommand()
command.operation = proto.PbOperation.DETACH
@ -235,13 +222,16 @@ def detach_all():
return {"status": result.status, "msg": result.msg}
def eject_by_id(scsi_id):
def eject_by_id(scsi_id, un=None):
"""
Takes int scsi_id and sends an EJECT command to the server.
Takes int scsi_id and optional int un.
Sends an EJECT command to the server.
Returns boolean status and str msg.
"""
devices = proto.PbDeviceDefinition()
devices.id = int(scsi_id)
if un != None:
devices.unit = int(un)
command = proto.PbCommand()
command.operation = proto.PbOperation.EJECT
@ -253,9 +243,10 @@ def eject_by_id(scsi_id):
return {"status": result.status, "msg": result.msg}
def list_devices(scsi_id=None):
def list_devices(scsi_id=None, un=None):
"""
Takes optional int scsi_id and sends a DEVICES_INFO command to the server.
Takes optional int scsi_id and optional int un.
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.
@ -270,6 +261,8 @@ def list_devices(scsi_id=None):
if scsi_id != None:
device = proto.PbDeviceDefinition()
device.id = int(scsi_id)
if un != None:
device.unit = int(un)
command.devices.append(device)
data = send_pb_command(command.SerializeToString())

View File

@ -35,6 +35,9 @@
<tbody>
<tr>
<td><b>ID</b></td>
{% if luns %}
<td><b>LUN</b></td>
{% endif %}
<td><b>Type</b></td>
<td><b>Status</b></td>
<td><b>File</b></td>
@ -45,6 +48,9 @@
<tr>
{% if device["id"] not in reserved_scsi_ids %}
<td style="text-align:center">{{device.id}}</td>
{% if luns %}
<td style="text-align:center">{{device.un}}</td>
{% endif %}
<td style="text-align:center">{{device.device_type}}</td>
<td style="text-align:center">{{device.status}}</td>
<td style="text-align:left">{{device.file}}</td>
@ -57,25 +63,32 @@
{% if device.device_type in removable_device_types and "No Media" not in device.status %}
<form action="/scsi/eject" method="post" onsubmit="return confirm('Eject Disk?')">
<input type="hidden" name="scsi_id" value="{{device.id}}">
<input type="hidden" name="un" value="{{device.un}}">
<input type="submit" value="Eject" />
</form>
<form action="/scsi/info" method="post">
<input type="hidden" name="scsi_id" value="{{device.id}}">
<input type="hidden" name="un" value="{{device.un}}">
<input type="submit" value="Info" />
</form>
{% else %}
<form action="/scsi/detach" method="post" onsubmit="return confirm('Detach Disk?')">
<input type="hidden" name="scsi_id" value="{{device.id}}">
<input type="hidden" name="un" value="{{device.un}}">
<input type="submit" value="Detach" />
</form>
<form action="/scsi/info" method="post">
<input type="hidden" name="scsi_id" value="{{device.id}}">
<input type="hidden" name="un" value="{{device.un}}">
<input type="submit" value="Info" />
</form>
{% endif %}
</td>
{% else %}
<td class="inactive">{{device.id}}</td>
{% if luns %}
<td class="inactive"></td>
{% endif %}
<td class="inactive"></td>
<td class="inactive">Reserved ID</td>
<td class="inactive"></td>
@ -93,6 +106,7 @@
<summary>Valid Image Files</summary>
<ul>
<li>A list of image files in <tt>{{base_dir}}</tt> that RaSCSI has identified as valid.</li>
<li>Select a valid SCSI ID and <a href="https://en.wikipedia.org/wiki/Logical_unit_number">LUN</a> to attach to. Unless you know what you're doing, always use LUN 0.</li>
<li>If RaSCSI was unable to detect the device type associated with the image, you can choose the type from the dropdown.</li>
<li>Types: SAHD = SASI HDD | SCHD = SCSI HDD | SCRM = Removable | SCMO = Magneto-Optical | SCCD = CD-ROM | SCBR = Host Bridge | SCDP = DaynaPORT</li>
</ul>
@ -118,17 +132,20 @@
<form action="/scsi/attach" method="post">
<input type="hidden" name="file_name" value="{{file["name"]}}">
<input type="hidden" name="file_size" value="{{file["size"]}}">
<label for="id">ID</label>
<select name="scsi_id">
{% for id in scsi_ids %}
<option value="{{id}}">{{id}}</option>
<option name="id" value="{{id}}"{% if id == recommended_id %} selected{% endif %}>{{id}}</option>
{% endfor %}
</select>
<label for="un">LUN</label>
<input type="text" name="un" size="1" value="0" />
{% if file["detected_type"] != "UNDEFINED" %}
<input type="hidden" name="type" value="{{file["detected_type"]}}">
{{file["detected_type"]}}
{% else %}
<select name="type">
<option value="" selected>????</option>
<option value="" selected>Type</option>
{% for d in device_types %}
<option value="{{d}}">{{d}}</option>
{% endfor %}
@ -184,7 +201,7 @@
<label for="scsi_id">SCSI ID:</label>
<select name="scsi_id">
{% for id in scsi_ids %}
<option value="{{id}}">{{id}}</option>
<option value="{{id}}"{% if id == recommended_id %} selected{% endif %}>{{id}}</option>
{% endfor %}
</select>
<input type="submit" value="Attach" />
@ -262,7 +279,7 @@
<form action="/files/download_to_iso" method="post">
<select name="scsi_id">
{% for id in scsi_ids %}
<option value="{{id}}">{{id}}</option>
<option value="{{id}}"{% if id == recommended_id %} selected{% endif %}>{{id}}</option>
{% endfor %}
</select>
<label for="url">URL:</label>

View File

@ -46,7 +46,6 @@ from ractl_cmds import (
get_server_info,
get_network_info,
get_device_types,
validate_scsi_id,
set_log_level,
)
from settings import *
@ -70,9 +69,13 @@ def index():
sorted_image_files = sorted(files["files"], key = lambda x: x["name"].lower())
sorted_config_files = sorted(config_files, key = lambda x: x.lower())
luns = 0
for d in devices["device_list"]:
luns += int(d["un"])
reserved_scsi_ids = server_info["reserved_ids"]
scsi_ids, recommended_id = get_valid_scsi_ids(devices["device_list"], reserved_scsi_ids)
formatted_devices = sort_and_format_devices(devices["device_list"])
scsi_ids = get_valid_scsi_ids(devices["device_list"], reserved_scsi_ids)
valid_file_suffix = "."+", .".join(
server_info["sahd"] +
@ -95,6 +98,8 @@ def index():
base_dir=base_dir,
cfg_dir=cfg_dir,
scsi_ids=scsi_ids,
recommended_id=recommended_id,
luns=luns,
reserved_scsi_ids=reserved_scsi_ids,
max_file_size=int(MAX_FILE_SIZE / 1024 / 1024),
running_env=running_env(),
@ -319,17 +324,12 @@ def daynaport_attach():
arg += (":" + ip + "/" + mask)
kwargs["interfaces"] = arg
validate = validate_scsi_id(scsi_id)
if validate["status"] == False:
flash(validate["msg"], "error")
return redirect(url_for("index"))
process = attach_image(scsi_id, **kwargs)
if process["status"] == True:
flash(f"Attached DaynaPORT to SCSI id {scsi_id}!")
flash(f"Attached DaynaPORT to SCSI ID {scsi_id}!")
return redirect(url_for("index"))
else:
flash(f"Failed to attach DaynaPORT to SCSI id {scsi_id}!", "error")
flash(f"Failed to attach DaynaPORT to SCSI ID {scsi_id}!", "error")
flash(process["msg"], "error")
return redirect(url_for("index"))
@ -339,14 +339,10 @@ def attach():
file_name = request.form.get("file_name")
file_size = request.form.get("file_size")
scsi_id = request.form.get("scsi_id")
un = request.form.get("un")
device_type = request.form.get("type")
validate = validate_scsi_id(scsi_id)
if validate["status"] == False:
flash(validate["msg"], "error")
return redirect(url_for("index"))
kwargs = {"image": file_name}
kwargs = {"unit": int(un), "image": file_name}
if device_type != "":
kwargs["device_type"] = device_type
@ -359,7 +355,8 @@ def attach():
if drive_properties.is_file():
process = read_drive_properties(str(drive_properties))
if process["status"] == False:
flash(f"Failed to load the device properties file {file_name_base}.{PROPERTIES_SUFFIX}", "error")
flash(f"Failed to load the device properties \
file {file_name_base}.{PROPERTIES_SUFFIX}", "error")
flash(process["msg"], "error")
return redirect(url_for("index"))
conf = process["conf"]
@ -370,10 +367,10 @@ def attach():
process = attach_image(scsi_id, **kwargs)
if process["status"] == True:
flash(f"Attached {file_name} to SCSI id {scsi_id}!")
flash(f"Attached {file_name} to SCSI ID {scsi_id} LUN {un}!")
return redirect(url_for("index"))
else:
flash(f"Failed to attach {file_name} to SCSI id {scsi_id}!", "error")
flash(f"Failed to attach {file_name} to SCSI ID {scsi_id} LUN {un}!", "error")
flash(process["msg"], "error")
return redirect(url_for("index"))
@ -393,12 +390,13 @@ def detach_all_devices():
@app.route("/scsi/detach", methods=["POST"])
def detach():
scsi_id = request.form.get("scsi_id")
process = detach_by_id(scsi_id)
un = request.form.get("un")
process = detach_by_id(scsi_id, un)
if process["status"] == True:
flash(f"Detached SCSI id {scsi_id}!")
flash(f"Detached SCSI ID {scsi_id} LUN {un}!")
return redirect(url_for("index"))
else:
flash(f"Failed to detach SCSI id {scsi_id}!", "error")
flash(f"Failed to detach SCSI ID {scsi_id} LUN {un}!", "error")
flash(process["msg"], "error")
return redirect(url_for("index"))
@ -406,24 +404,27 @@ def detach():
@app.route("/scsi/eject", methods=["POST"])
def eject():
scsi_id = request.form.get("scsi_id")
process = eject_by_id(scsi_id)
un = request.form.get("un")
process = eject_by_id(scsi_id, un)
if process["status"] == True:
flash(f"Ejected scsi id {scsi_id}!")
flash(f"Ejected SCSI ID {scsi_id} LUN {un}!")
return redirect(url_for("index"))
else:
flash(f"Failed to eject SCSI id {scsi_id}!", "error")
flash(f"Failed to eject SCSI ID {scsi_id} LUN {un}!", "error")
flash(process["msg"], "error")
return redirect(url_for("index"))
@app.route("/scsi/info", methods=["POST"])
def device_info():
scsi_id = request.form.get("scsi_id")
un = request.form.get("un")
devices = list_devices(scsi_id)
devices = list_devices(scsi_id, un)
# First check if any device at all was returned
if devices["status"] == False:
flash(f"No device attached to SCSI id {scsi_id}!", "error")
flash(f"No device attached to SCSI ID {scsi_id} LUN {un}!", "error")
return redirect(url_for("index"))
# Looking at the first dict in list to get
# the one and only device that should have been returned
@ -431,7 +432,7 @@ def device_info():
if str(device["id"]) == scsi_id:
flash("=== DEVICE INFO ===")
flash(f"SCSI ID: {device['id']}")
flash(f"Unit: {device['un']}")
flash(f"LUN: {device['un']}")
flash(f"Type: {device['device_type']}")
flash(f"Status: {device['status']}")
flash(f"File: {device['image']}")
@ -442,7 +443,7 @@ def device_info():
flash(f"Block Size: {device['block_size']}")
return redirect(url_for("index"))
else:
flash(f"Failed to get device info for SCSI id {scsi_id}!", "error")
flash(f"Failed to get device info for SCSI ID {scsi_id} LUN {un}!", "error")
return redirect(url_for("index"))
@app.route("/pi/reboot", methods=["POST"])
@ -469,19 +470,15 @@ def shutdown():
@app.route("/files/download_to_iso", methods=["POST"])
def download_file():
scsi_id = request.form.get("scsi_id")
validate = validate_scsi_id(scsi_id)
if validate["status"] == False:
flash(validate["msg"], "error")
return redirect(url_for("index"))
url = request.form.get("url")
process = download_file_to_iso(scsi_id, url)
if process["status"] == True:
flash(f"File Downloaded and Attached to SCSI id {scsi_id}")
flash(f"File Downloaded and Attached to SCSI ID {scsi_id}")
flash(process["msg"])
return redirect(url_for("index"))
else:
flash(f"Failed to download and attach file {url}", "error")
flash(f"Failed to download and/or attach file {url}", "error")
flash(process["msg"], "error")
return redirect(url_for("index"))
@ -647,7 +644,7 @@ def show_properties():
if process["status"]:
flash("=== DRIVE PROPERTIES ===")
flash(f"File Name: {file_name}")
flash(f"File Name: {cfg_dir}{file_name}")
flash(f"Vendor: {prop['vendor']}")
flash(f"Product: {prop['product']}")
flash(f"Revision: {prop['revision']}")