mirror of https://github.com/akuker/RASCSI.git
Add statistics and make scsictl accept generic key/value parameters (#1237/#1238) (#1262)
* Add statistics and make scsictl accept generic key/value parameters
This commit is contained in:
parent
8f45e4f491
commit
b7cb23e391
|
@ -273,6 +273,9 @@ bool Disk::Eject(bool force)
|
||||||
|
|
||||||
// The image file for this drive is not in use anymore
|
// The image file for this drive is not in use anymore
|
||||||
UnreserveFile();
|
UnreserveFile();
|
||||||
|
|
||||||
|
sector_read_count = 0;
|
||||||
|
sector_write_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
@ -506,6 +509,8 @@ int Disk::Read(span<uint8_t> buf, uint64_t block)
|
||||||
throw scsi_exception(sense_key::medium_error, asc::read_fault);
|
throw scsi_exception(sense_key::medium_error, asc::read_fault);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
++sector_read_count;
|
||||||
|
|
||||||
return GetSectorSizeInBytes();
|
return GetSectorSizeInBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -518,6 +523,8 @@ void Disk::Write(span<const uint8_t> buf, uint64_t block)
|
||||||
if (!cache->WriteSector(buf, static_cast<uint32_t>(block))) {
|
if (!cache->WriteSector(buf, static_cast<uint32_t>(block))) {
|
||||||
throw scsi_exception(sense_key::medium_error, asc::write_fault);
|
throw scsi_exception(sense_key::medium_error, asc::write_fault);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
++sector_write_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Disk::Seek()
|
void Disk::Seek()
|
||||||
|
@ -711,3 +718,35 @@ bool Disk::SetConfiguredSectorSize(const DeviceFactory& device_factory, uint32_t
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vector<PbStatistics> Disk::GetStatistics() const
|
||||||
|
{
|
||||||
|
vector<PbStatistics> statistics = PrimaryDevice::GetStatistics();
|
||||||
|
|
||||||
|
// Enrich cache statistics with device information before adding them to device statistics
|
||||||
|
if (cache) {
|
||||||
|
for (auto& s : cache->GetStatistics(IsReadOnly())) {
|
||||||
|
s.set_id(GetId());
|
||||||
|
s.set_unit(GetLun());
|
||||||
|
statistics.push_back(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PbStatistics s;
|
||||||
|
s.set_id(GetId());
|
||||||
|
s.set_unit(GetLun());
|
||||||
|
|
||||||
|
s.set_category(PbStatisticsCategory::CATEGORY_INFO);
|
||||||
|
|
||||||
|
s.set_key(SECTOR_READ_COUNT);
|
||||||
|
s.set_value(sector_read_count);
|
||||||
|
statistics.push_back(s);
|
||||||
|
|
||||||
|
if (!IsReadOnly()) {
|
||||||
|
s.set_key(SECTOR_WRITE_COUNT);
|
||||||
|
s.set_value(sector_write_count);
|
||||||
|
statistics.push_back(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return statistics;
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
// XM6i
|
// XM6i
|
||||||
// Copyright (C) 2010-2015 isaki@NetBSD.org
|
// Copyright (C) 2010-2015 isaki@NetBSD.org
|
||||||
//
|
//
|
||||||
// Imported sava's Anex86/T98Next image and MO format support patch.
|
|
||||||
// Comments translated to english by akuker.
|
// Comments translated to english by akuker.
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
@ -16,6 +15,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "shared/scsi.h"
|
#include "shared/scsi.h"
|
||||||
|
#include "shared/piscsi_util.h"
|
||||||
#include "device_factory.h"
|
#include "device_factory.h"
|
||||||
#include "disk_track.h"
|
#include "disk_track.h"
|
||||||
#include "disk_cache.h"
|
#include "disk_cache.h"
|
||||||
|
@ -42,6 +42,12 @@ class Disk : public StorageDevice, private ScsiBlockCommands
|
||||||
// Sector size shift count (9=512, 10=1024, 11=2048, 12=4096)
|
// Sector size shift count (9=512, 10=1024, 11=2048, 12=4096)
|
||||||
uint32_t size_shift_count = 0;
|
uint32_t size_shift_count = 0;
|
||||||
|
|
||||||
|
uint64_t sector_read_count = 0;
|
||||||
|
uint64_t sector_write_count = 0;
|
||||||
|
|
||||||
|
inline static const string SECTOR_READ_COUNT = "sector_read_count";
|
||||||
|
inline static const string SECTOR_WRITE_COUNT = "sector_write_count";
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
using StorageDevice::StorageDevice;
|
using StorageDevice::StorageDevice;
|
||||||
|
@ -62,6 +68,8 @@ public:
|
||||||
bool SetConfiguredSectorSize(const DeviceFactory&, uint32_t);
|
bool SetConfiguredSectorSize(const DeviceFactory&, uint32_t);
|
||||||
void FlushCache() override;
|
void FlushCache() override;
|
||||||
|
|
||||||
|
vector<PbStatistics> GetStatistics() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// Commands covered by the SCSI specifications (see https://www.t10.org/drafts.htm)
|
// Commands covered by the SCSI specifications (see https://www.t10.org/drafts.htm)
|
||||||
|
|
|
@ -27,11 +27,11 @@ DiskCache::DiskCache(const string& path, int size, uint32_t blocks, off_t imgoff
|
||||||
assert(imgoff >= 0);
|
assert(imgoff >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DiskCache::Save() const
|
bool DiskCache::Save()
|
||||||
{
|
{
|
||||||
// Save valid tracks
|
// Save valid tracks
|
||||||
return ranges::none_of(cache.begin(), cache.end(), [this](const cache_t& c)
|
return ranges::none_of(cache.begin(), cache.end(), [this](const cache_t& c)
|
||||||
{ return c.disktrk != nullptr && !c.disktrk->Save(sec_path); });
|
{ return c.disktrk != nullptr && !c.disktrk->Save(sec_path, cache_miss_write_count); });
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<DiskTrack> DiskCache::GetTrack(uint32_t block)
|
shared_ptr<DiskTrack> DiskCache::GetTrack(uint32_t block)
|
||||||
|
@ -120,7 +120,7 @@ shared_ptr<DiskTrack> DiskCache::Assign(int track)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save this track
|
// Save this track
|
||||||
if (!cache[c].disktrk->Save(sec_path)) {
|
if (!cache[c].disktrk->Save(sec_path, cache_miss_write_count)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,17 +156,16 @@ bool DiskCache::Load(int index, int track, shared_ptr<DiskTrack> disktrk)
|
||||||
sectors = 0x100;
|
sectors = 0x100;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a disk track
|
|
||||||
if (disktrk == nullptr) {
|
if (disktrk == nullptr) {
|
||||||
disktrk = make_shared<DiskTrack>();
|
disktrk = make_shared<DiskTrack>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize disk track
|
|
||||||
disktrk->Init(track, sec_size, sectors, cd_raw, imgoffset);
|
disktrk->Init(track, sec_size, sectors, cd_raw, imgoffset);
|
||||||
|
|
||||||
// Try loading
|
// Try loading
|
||||||
if (!disktrk->Load(sec_path)) {
|
if (!disktrk->Load(sec_path, cache_miss_read_count)) {
|
||||||
// Failure
|
++read_error_count;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,3 +189,35 @@ void DiskCache::UpdateSerialNumber()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vector<PbStatistics> DiskCache::GetStatistics(bool is_read_only) const
|
||||||
|
{
|
||||||
|
vector<PbStatistics> statistics;
|
||||||
|
|
||||||
|
PbStatistics s;
|
||||||
|
|
||||||
|
s.set_category(PbStatisticsCategory::CATEGORY_INFO);
|
||||||
|
|
||||||
|
s.set_key(CACHE_MISS_READ_COUNT);
|
||||||
|
s.set_value(cache_miss_read_count);
|
||||||
|
statistics.push_back(s);
|
||||||
|
|
||||||
|
if (!is_read_only) {
|
||||||
|
s.set_key(CACHE_MISS_WRITE_COUNT);
|
||||||
|
s.set_value(cache_miss_write_count);
|
||||||
|
statistics.push_back(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
s.set_category(PbStatisticsCategory::CATEGORY_ERROR);
|
||||||
|
|
||||||
|
s.set_key(READ_ERROR_COUNT);
|
||||||
|
s.set_value(read_error_count);
|
||||||
|
statistics.push_back(s);
|
||||||
|
|
||||||
|
if (!is_read_only) {
|
||||||
|
s.set_key(WRITE_ERROR_COUNT);
|
||||||
|
s.set_value(write_error_count);
|
||||||
|
statistics.push_back(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return statistics;
|
||||||
|
}
|
||||||
|
|
|
@ -15,18 +15,30 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "generated/piscsi_interface.pb.h"
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace piscsi_interface;
|
||||||
|
|
||||||
class DiskCache
|
class DiskCache
|
||||||
{
|
{
|
||||||
// Number of tracks to cache
|
// Number of tracks to cache
|
||||||
static const int CACHE_MAX = 16;
|
static const int CACHE_MAX = 16;
|
||||||
|
|
||||||
|
uint64_t read_error_count = 0;
|
||||||
|
uint64_t write_error_count = 0;
|
||||||
|
uint64_t cache_miss_read_count = 0;
|
||||||
|
uint64_t cache_miss_write_count = 0;
|
||||||
|
|
||||||
|
inline static const string READ_ERROR_COUNT = "read_error_count";
|
||||||
|
inline static const string WRITE_ERROR_COUNT = "write_error_count";
|
||||||
|
inline static const string CACHE_MISS_READ_COUNT = "cache_miss_read_count";
|
||||||
|
inline static const string CACHE_MISS_WRITE_COUNT = "cache_miss_write_count";
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// Internal data definition
|
// Internal data definition
|
||||||
|
@ -40,11 +52,12 @@ public:
|
||||||
|
|
||||||
void SetRawMode(bool b) { cd_raw = b; } // CD-ROM raw mode setting
|
void SetRawMode(bool b) { cd_raw = b; } // CD-ROM raw mode setting
|
||||||
|
|
||||||
// Access
|
bool Save(); // Save and release all
|
||||||
bool Save() const; // Save and release all
|
|
||||||
bool ReadSector(span<uint8_t>, uint32_t); // Sector Read
|
bool ReadSector(span<uint8_t>, uint32_t); // Sector Read
|
||||||
bool WriteSector(span<const uint8_t>, uint32_t); // Sector Write
|
bool WriteSector(span<const uint8_t>, uint32_t); // Sector Write
|
||||||
|
|
||||||
|
vector<PbStatistics> GetStatistics(bool) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// Internal Management
|
// Internal Management
|
||||||
|
|
|
@ -48,7 +48,7 @@ void DiskTrack::Init(int track, int size, int sectors, bool raw, off_t imgoff)
|
||||||
dt.imgoffset = imgoff;
|
dt.imgoffset = imgoff;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DiskTrack::Load(const string& path)
|
bool DiskTrack::Load(const string& path, uint64_t& cache_miss_read_count)
|
||||||
{
|
{
|
||||||
// Not needed if already loaded
|
// Not needed if already loaded
|
||||||
if (dt.init) {
|
if (dt.init) {
|
||||||
|
@ -56,6 +56,8 @@ bool DiskTrack::Load(const string& path)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
++cache_miss_read_count;
|
||||||
|
|
||||||
// Calculate offset (previous tracks are considered to hold 256 sectors)
|
// Calculate offset (previous tracks are considered to hold 256 sectors)
|
||||||
off_t offset = ((off_t)dt.track << 8);
|
off_t offset = ((off_t)dt.track << 8);
|
||||||
if (dt.raw) {
|
if (dt.raw) {
|
||||||
|
@ -138,7 +140,7 @@ bool DiskTrack::Load(const string& path)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DiskTrack::Save(const string& path)
|
bool DiskTrack::Save(const string& path, uint64_t& cache_miss_write_count)
|
||||||
{
|
{
|
||||||
// Not needed if not initialized
|
// Not needed if not initialized
|
||||||
if (!dt.init) {
|
if (!dt.init) {
|
||||||
|
@ -150,6 +152,8 @@ bool DiskTrack::Save(const string& path)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
++cache_miss_write_count;
|
||||||
|
|
||||||
// Need to write
|
// Need to write
|
||||||
assert(dt.buffer);
|
assert(dt.buffer);
|
||||||
assert((dt.sectors > 0) && (dt.sectors <= 0x100));
|
assert((dt.sectors > 0) && (dt.sectors <= 0x100));
|
||||||
|
|
|
@ -50,10 +50,9 @@ private:
|
||||||
friend class DiskCache;
|
friend class DiskCache;
|
||||||
|
|
||||||
void Init(int track, int size, int sectors, bool raw = false, off_t imgoff = 0);
|
void Init(int track, int size, int sectors, bool raw = false, off_t imgoff = 0);
|
||||||
bool Load(const string& path);
|
bool Load(const string& path, uint64_t&);
|
||||||
bool Save(const string& path);
|
bool Save(const string& path, uint64_t&);
|
||||||
|
|
||||||
// Read / Write
|
|
||||||
bool ReadSector(span<uint8_t>, int) const; // Sector Read
|
bool ReadSector(span<uint8_t>, int) const; // Sector Read
|
||||||
bool WriteSector(span<const uint8_t> buf, int); // Sector Write
|
bool WriteSector(span<const uint8_t> buf, int); // Sector Write
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,13 @@ public:
|
||||||
void Reset() override;
|
void Reset() override;
|
||||||
|
|
||||||
virtual void FlushCache() {
|
virtual void FlushCache() {
|
||||||
// Devices with a cache have to implement this method
|
// Devices with a cache have to override this method
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual vector<PbStatistics> GetStatistics() const {
|
||||||
|
// Devices which provide statistics have to override this method
|
||||||
|
|
||||||
|
return vector<PbStatistics>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -119,7 +119,7 @@ vector<uint8_t> SCSIDaynaPort::InquiryInternal() const
|
||||||
// - The SCSI/Link apparently has about 6KB buffer space for packets.
|
// - The SCSI/Link apparently has about 6KB buffer space for packets.
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
int SCSIDaynaPort::Read(cdb_t cdb, vector<uint8_t>& buf, uint64_t) const
|
int SCSIDaynaPort::Read(cdb_t cdb, vector<uint8_t>& buf, uint64_t)
|
||||||
{
|
{
|
||||||
int rx_packet_size = 0;
|
int rx_packet_size = 0;
|
||||||
const auto response = (scsi_resp_read_t*)buf.data();
|
const auto response = (scsi_resp_read_t*)buf.data();
|
||||||
|
@ -155,6 +155,8 @@ int SCSIDaynaPort::Read(cdb_t cdb, vector<uint8_t>& buf, uint64_t) const
|
||||||
return DAYNAPORT_READ_HEADER_SZ;
|
return DAYNAPORT_READ_HEADER_SZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
byte_read_count += rx_packet_size * read_count;
|
||||||
|
|
||||||
LogTrace("Packet Size " + to_string(rx_packet_size) + ", read count: " + to_string(read_count));
|
LogTrace("Packet Size " + to_string(rx_packet_size) + ", read count: " + to_string(read_count));
|
||||||
|
|
||||||
// This is a very basic filter to prevent unnecessary packets from
|
// This is a very basic filter to prevent unnecessary packets from
|
||||||
|
@ -252,17 +254,19 @@ int SCSIDaynaPort::Read(cdb_t cdb, vector<uint8_t>& buf, uint64_t) const
|
||||||
// XX XX ... is the actual packet
|
// XX XX ... is the actual packet
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
bool SCSIDaynaPort::Write(cdb_t cdb, span<const uint8_t> buf) const
|
bool SCSIDaynaPort::Write(cdb_t cdb, span<const uint8_t> buf)
|
||||||
{
|
{
|
||||||
if (const int data_format = cdb[5]; data_format == 0x00) {
|
if (const int data_format = cdb[5]; data_format == 0x00) {
|
||||||
const int data_length = GetInt16(cdb, 3);
|
const int data_length = GetInt16(cdb, 3);
|
||||||
tap.Send(buf.data(), data_length);
|
tap.Send(buf.data(), data_length);
|
||||||
|
byte_write_count += data_length;
|
||||||
LogTrace("Transmitted " + to_string(data_length) + " byte(s) (00 format)");
|
LogTrace("Transmitted " + to_string(data_length) + " byte(s) (00 format)");
|
||||||
}
|
}
|
||||||
else if (data_format == 0x80) {
|
else if (data_format == 0x80) {
|
||||||
// The data length is specified in the first 2 bytes of the payload
|
// The data length is specified in the first 2 bytes of the payload
|
||||||
const int data_length = buf[1] + ((static_cast<int>(buf[0]) & 0xff) << 8);
|
const int data_length = buf[1] + ((static_cast<int>(buf[0]) & 0xff) << 8);
|
||||||
tap.Send(&(buf.data()[4]), data_length);
|
tap.Send(&(buf.data()[4]), data_length);
|
||||||
|
byte_write_count += data_length;
|
||||||
LogTrace("Transmitted " + to_string(data_length) + "byte(s) (80 format)");
|
LogTrace("Transmitted " + to_string(data_length) + "byte(s) (80 format)");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -305,7 +309,7 @@ void SCSIDaynaPort::TestUnitReady()
|
||||||
EnterStatusPhase();
|
EnterStatusPhase();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SCSIDaynaPort::Read6() const
|
void SCSIDaynaPort::Read6()
|
||||||
{
|
{
|
||||||
// Get record number and block number
|
// Get record number and block number
|
||||||
const uint32_t record = GetInt24(GetController()->GetCmd(), 1) & 0x1fffff;
|
const uint32_t record = GetInt24(GetController()->GetCmd(), 1) & 0x1fffff;
|
||||||
|
@ -478,3 +482,23 @@ void SCSIDaynaPort::EnableInterface() const
|
||||||
EnterStatusPhase();
|
EnterStatusPhase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vector<PbStatistics> SCSIDaynaPort::GetStatistics() const
|
||||||
|
{
|
||||||
|
vector<PbStatistics> statistics = PrimaryDevice::GetStatistics();
|
||||||
|
|
||||||
|
PbStatistics s;
|
||||||
|
s.set_id(GetId());
|
||||||
|
s.set_unit(GetLun());
|
||||||
|
|
||||||
|
s.set_category(PbStatisticsCategory::CATEGORY_INFO);
|
||||||
|
|
||||||
|
s.set_key(BYTE_READ_COUNT);
|
||||||
|
s.set_value(byte_read_count);
|
||||||
|
statistics.push_back(s);
|
||||||
|
|
||||||
|
s.set_key(BYTE_WRITE_COUNT);
|
||||||
|
s.set_value(byte_write_count);
|
||||||
|
statistics.push_back(s);
|
||||||
|
|
||||||
|
return statistics;
|
||||||
|
}
|
||||||
|
|
|
@ -44,6 +44,12 @@
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
class SCSIDaynaPort : public PrimaryDevice
|
class SCSIDaynaPort : public PrimaryDevice
|
||||||
{
|
{
|
||||||
|
uint64_t byte_read_count = 0;
|
||||||
|
uint64_t byte_write_count = 0;
|
||||||
|
|
||||||
|
inline static const string BYTE_READ_COUNT = "byte_read_count";
|
||||||
|
inline static const string BYTE_WRITE_COUNT = "byte_write_count";
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
explicit SCSIDaynaPort(int);
|
explicit SCSIDaynaPort(int);
|
||||||
|
@ -56,19 +62,21 @@ public:
|
||||||
|
|
||||||
// Commands
|
// Commands
|
||||||
vector<uint8_t> InquiryInternal() const override;
|
vector<uint8_t> InquiryInternal() const override;
|
||||||
int Read(cdb_t, vector<uint8_t>&, uint64_t) const;
|
int Read(cdb_t, vector<uint8_t>&, uint64_t);
|
||||||
bool Write(cdb_t, span<const uint8_t>) const;
|
bool Write(cdb_t, span<const uint8_t>);
|
||||||
|
|
||||||
int RetrieveStats(cdb_t, vector<uint8_t>&) const;
|
int RetrieveStats(cdb_t, vector<uint8_t>&) const;
|
||||||
|
|
||||||
void TestUnitReady() override;
|
void TestUnitReady() override;
|
||||||
void Read6() const;
|
void Read6();
|
||||||
void Write6() const;
|
void Write6() const;
|
||||||
void RetrieveStatistics() const;
|
void RetrieveStatistics() const;
|
||||||
void SetInterfaceMode() const;
|
void SetInterfaceMode() const;
|
||||||
void SetMcastAddr() const;
|
void SetMcastAddr() const;
|
||||||
void EnableInterface() const;
|
void EnableInterface() const;
|
||||||
|
|
||||||
|
vector<PbStatistics> GetStatistics() const override;
|
||||||
|
|
||||||
static const int DAYNAPORT_BUFFER_SIZE = 0x1000000;
|
static const int DAYNAPORT_BUFFER_SIZE = 0x1000000;
|
||||||
|
|
||||||
static const int CMD_SCSILINK_STATS = 0x09;
|
static const int CMD_SCSILINK_STATS = 0x09;
|
||||||
|
|
|
@ -115,6 +115,8 @@ void SCSIPrinter::Print()
|
||||||
LogError("Transfer buffer overflow: Buffer size is " + to_string(GetController()->GetBuffer().size()) +
|
LogError("Transfer buffer overflow: Buffer size is " + to_string(GetController()->GetBuffer().size()) +
|
||||||
" bytes, " + to_string(length) + " bytes expected");
|
" bytes, " + to_string(length) + " bytes expected");
|
||||||
|
|
||||||
|
++print_error_count;
|
||||||
|
|
||||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
|
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,6 +131,8 @@ void SCSIPrinter::SynchronizeBuffer()
|
||||||
if (!out.is_open()) {
|
if (!out.is_open()) {
|
||||||
LogWarn("Nothing to print");
|
LogWarn("Nothing to print");
|
||||||
|
|
||||||
|
++print_warning_count;
|
||||||
|
|
||||||
throw scsi_exception(sense_key::aborted_command);
|
throw scsi_exception(sense_key::aborted_command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,6 +149,8 @@ void SCSIPrinter::SynchronizeBuffer()
|
||||||
if (system(cmd.c_str())) {
|
if (system(cmd.c_str())) {
|
||||||
LogError("Printing file '" + filename + "' failed, the printing system might not be configured");
|
LogError("Printing file '" + filename + "' failed, the printing system might not be configured");
|
||||||
|
|
||||||
|
++print_error_count;
|
||||||
|
|
||||||
CleanUp();
|
CleanUp();
|
||||||
|
|
||||||
throw scsi_exception(sense_key::aborted_command);
|
throw scsi_exception(sense_key::aborted_command);
|
||||||
|
@ -157,6 +163,8 @@ void SCSIPrinter::SynchronizeBuffer()
|
||||||
|
|
||||||
bool SCSIPrinter::WriteByteSequence(span<const uint8_t> buf)
|
bool SCSIPrinter::WriteByteSequence(span<const uint8_t> buf)
|
||||||
{
|
{
|
||||||
|
byte_receive_count += buf.size();
|
||||||
|
|
||||||
if (!out.is_open()) {
|
if (!out.is_open()) {
|
||||||
vector<char> f(file_template.begin(), file_template.end());
|
vector<char> f(file_template.begin(), file_template.end());
|
||||||
f.push_back(0);
|
f.push_back(0);
|
||||||
|
@ -165,6 +173,9 @@ bool SCSIPrinter::WriteByteSequence(span<const uint8_t> buf)
|
||||||
const int fd = mkstemp(f.data());
|
const int fd = mkstemp(f.data());
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
LogError("Can't create printer output file for pattern '" + filename + "': " + strerror(errno));
|
LogError("Can't create printer output file for pattern '" + filename + "': " + strerror(errno));
|
||||||
|
|
||||||
|
++print_error_count;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
close(fd);
|
close(fd);
|
||||||
|
@ -173,6 +184,8 @@ bool SCSIPrinter::WriteByteSequence(span<const uint8_t> buf)
|
||||||
|
|
||||||
out.open(filename, ios::binary);
|
out.open(filename, ios::binary);
|
||||||
if (out.fail()) {
|
if (out.fail()) {
|
||||||
|
++print_error_count;
|
||||||
|
|
||||||
throw scsi_exception(sense_key::aborted_command);
|
throw scsi_exception(sense_key::aborted_command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,5 +196,43 @@ bool SCSIPrinter::WriteByteSequence(span<const uint8_t> buf)
|
||||||
|
|
||||||
out.write((const char *)buf.data(), buf.size());
|
out.write((const char *)buf.data(), buf.size());
|
||||||
|
|
||||||
return !out.fail();
|
const bool status = out.fail();
|
||||||
|
if (!status) {
|
||||||
|
++print_error_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !status;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<PbStatistics> SCSIPrinter::GetStatistics() const
|
||||||
|
{
|
||||||
|
vector<PbStatistics> statistics = PrimaryDevice::GetStatistics();
|
||||||
|
|
||||||
|
PbStatistics s;
|
||||||
|
s.set_id(GetId());
|
||||||
|
s.set_unit(GetLun());
|
||||||
|
|
||||||
|
s.set_category(PbStatisticsCategory::CATEGORY_INFO);
|
||||||
|
|
||||||
|
s.set_key(FILE_PRINT_COUNT);
|
||||||
|
s.set_value(file_print_count);
|
||||||
|
statistics.push_back(s);
|
||||||
|
|
||||||
|
s.set_key(BYTE_RECEIVE_COUNT);
|
||||||
|
s.set_value(byte_receive_count);
|
||||||
|
statistics.push_back(s);
|
||||||
|
|
||||||
|
s.set_category(PbStatisticsCategory::CATEGORY_ERROR);
|
||||||
|
|
||||||
|
s.set_key(PRINT_ERROR_COUNT);
|
||||||
|
s.set_value(print_error_count);
|
||||||
|
statistics.push_back(s);
|
||||||
|
|
||||||
|
s.set_category(PbStatisticsCategory::CATEGORY_WARNING);
|
||||||
|
|
||||||
|
s.set_key(PRINT_WARNING_COUNT);
|
||||||
|
s.set_value(print_warning_count);
|
||||||
|
statistics.push_back(s);
|
||||||
|
|
||||||
|
return statistics;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,10 +21,20 @@ using namespace std;
|
||||||
|
|
||||||
class SCSIPrinter : public PrimaryDevice, private ScsiPrinterCommands
|
class SCSIPrinter : public PrimaryDevice, private ScsiPrinterCommands
|
||||||
{
|
{
|
||||||
|
uint64_t file_print_count = 0;
|
||||||
|
uint64_t byte_receive_count = 0;
|
||||||
|
uint64_t print_error_count = 0;
|
||||||
|
uint64_t print_warning_count = 0;
|
||||||
|
|
||||||
static const int NOT_RESERVED = -2;
|
static const int NOT_RESERVED = -2;
|
||||||
|
|
||||||
static constexpr const char *PRINTER_FILE_PATTERN = "/piscsi_sclp-XXXXXX";
|
static constexpr const char *PRINTER_FILE_PATTERN = "/piscsi_sclp-XXXXXX";
|
||||||
|
|
||||||
|
inline static const string FILE_PRINT_COUNT = "file_print_count";
|
||||||
|
inline static const string BYTE_RECEIVE_COUNT = "byte_receive_count";
|
||||||
|
inline static const string PRINT_ERROR_COUNT = "print_error_count";
|
||||||
|
inline static const string PRINT_WARNING_COUNT = "print_warning_count";
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
explicit SCSIPrinter(int);
|
explicit SCSIPrinter(int);
|
||||||
|
@ -39,6 +49,8 @@ public:
|
||||||
|
|
||||||
bool WriteByteSequence(span<const uint8_t>) override;
|
bool WriteByteSequence(span<const uint8_t>) override;
|
||||||
|
|
||||||
|
vector<PbStatistics> GetStatistics() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void TestUnitReady() override;
|
void TestUnitReady() override;
|
||||||
|
|
|
@ -357,9 +357,8 @@ bool Piscsi::ExecuteCommand(CommandContext& context)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SERVER_INFO:
|
case SERVER_INFO:
|
||||||
response.GetServerInfo(*result.mutable_server_info(), controller_manager.GetAllDevices(),
|
response.GetServerInfo(*result.mutable_server_info(), command, controller_manager.GetAllDevices(),
|
||||||
executor->GetReservedIds(), piscsi_image.GetDefaultFolder(),
|
executor->GetReservedIds(), piscsi_image.GetDefaultFolder(), piscsi_image.GetDepth());
|
||||||
GetParam(command, "folder_pattern"), GetParam(command, "file_pattern"), piscsi_image.GetDepth());
|
|
||||||
context.WriteSuccessResult(result);
|
context.WriteSuccessResult(result);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -408,6 +407,11 @@ bool Piscsi::ExecuteCommand(CommandContext& context)
|
||||||
context.WriteSuccessResult(result);
|
context.WriteSuccessResult(result);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case STATISTICS_INFO:
|
||||||
|
response.GetStatisticsInfo(*result.mutable_statistics_info(), controller_manager.GetAllDevices());
|
||||||
|
context.WriteSuccessResult(result);
|
||||||
|
break;
|
||||||
|
|
||||||
case OPERATION_INFO:
|
case OPERATION_INFO:
|
||||||
response.GetOperationInfo(*result.mutable_operation_info(), piscsi_image.GetDepth());
|
response.GetOperationInfo(*result.mutable_operation_info(), piscsi_image.GetDepth());
|
||||||
context.WriteSuccessResult(result);
|
context.WriteSuccessResult(result);
|
||||||
|
|
|
@ -227,19 +227,62 @@ void PiscsiResponse::GetDevicesInfo(const unordered_set<shared_ptr<PrimaryDevice
|
||||||
result.set_status(true);
|
result.set_status(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PiscsiResponse::GetServerInfo(PbServerInfo& server_info, const unordered_set<shared_ptr<PrimaryDevice>>& devices,
|
void PiscsiResponse::GetServerInfo(PbServerInfo& server_info, const PbCommand& command,
|
||||||
const unordered_set<int>& reserved_ids, const string& default_folder, const string& folder_pattern,
|
const unordered_set<shared_ptr<PrimaryDevice>>& devices, const unordered_set<int>& reserved_ids,
|
||||||
const string& file_pattern, int scan_depth) const
|
const string& default_folder, int scan_depth) const
|
||||||
{
|
{
|
||||||
GetVersionInfo(*server_info.mutable_version_info());
|
const vector<string> command_operations = Split(GetParam(command, "operations"), ',');
|
||||||
GetLogLevelInfo(*server_info.mutable_log_level_info());
|
set<string, less<>> operations;
|
||||||
GetDeviceTypesInfo(*server_info.mutable_device_types_info());
|
for (const string& operation : command_operations) {
|
||||||
GetAvailableImages(server_info, default_folder, folder_pattern, file_pattern, scan_depth);
|
string op;
|
||||||
GetNetworkInterfacesInfo(*server_info.mutable_network_interfaces_info());
|
ranges::transform(operation, back_inserter(op), ::toupper);
|
||||||
GetMappingInfo(*server_info.mutable_mapping_info());
|
operations.insert(op);
|
||||||
GetDevices(devices, server_info, default_folder);
|
}
|
||||||
GetReservedIds(*server_info.mutable_reserved_ids_info(), reserved_ids);
|
|
||||||
GetOperationInfo(*server_info.mutable_operation_info(), scan_depth);
|
if (!operations.empty()) {
|
||||||
|
spdlog::trace("Requested operation(s): " + Join(operations, ","));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasOperation(operations, PbOperation::VERSION_INFO)) {
|
||||||
|
GetVersionInfo(*server_info.mutable_version_info());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasOperation(operations, PbOperation::LOG_LEVEL_INFO)) {
|
||||||
|
GetLogLevelInfo(*server_info.mutable_log_level_info());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasOperation(operations, PbOperation::DEVICE_TYPES_INFO)) {
|
||||||
|
GetDeviceTypesInfo(*server_info.mutable_device_types_info());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasOperation(operations, PbOperation::DEFAULT_IMAGE_FILES_INFO)) {
|
||||||
|
GetAvailableImages(server_info, default_folder, GetParam(command, "folder_pattern"),
|
||||||
|
GetParam(command, "file_pattern"), scan_depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasOperation(operations, PbOperation::NETWORK_INTERFACES_INFO)) {
|
||||||
|
GetNetworkInterfacesInfo(*server_info.mutable_network_interfaces_info());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasOperation(operations, PbOperation::MAPPING_INFO)) {
|
||||||
|
GetMappingInfo(*server_info.mutable_mapping_info());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasOperation(operations, PbOperation::STATISTICS_INFO)) {
|
||||||
|
GetStatisticsInfo(*server_info.mutable_statistics_info(), devices);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasOperation(operations, PbOperation::DEVICES_INFO)) {
|
||||||
|
GetDevices(devices, server_info, default_folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasOperation(operations, PbOperation::RESERVED_IDS_INFO)) {
|
||||||
|
GetReservedIds(*server_info.mutable_reserved_ids_info(), reserved_ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasOperation(operations, PbOperation::OPERATION_INFO)) {
|
||||||
|
GetOperationInfo(*server_info.mutable_operation_info(), scan_depth);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PiscsiResponse::GetVersionInfo(PbVersionInfo& version_info) const
|
void PiscsiResponse::GetVersionInfo(PbVersionInfo& version_info) const
|
||||||
|
@ -272,6 +315,21 @@ void PiscsiResponse::GetMappingInfo(PbMappingInfo& mapping_info) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PiscsiResponse::GetStatisticsInfo(PbStatisticsInfo& statistics_info,
|
||||||
|
const unordered_set<shared_ptr<PrimaryDevice>>& devices) const
|
||||||
|
{
|
||||||
|
for (const auto& device : devices) {
|
||||||
|
for (const auto& statistics : device->GetStatistics()) {
|
||||||
|
auto s = statistics_info.add_statistics();
|
||||||
|
s->set_id(statistics.id());
|
||||||
|
s->set_unit(statistics.unit());
|
||||||
|
s->set_category(statistics.category());
|
||||||
|
s->set_key(statistics.key());
|
||||||
|
s->set_value(statistics.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PiscsiResponse::GetOperationInfo(PbOperationInfo& operation_info, int depth) const
|
void PiscsiResponse::GetOperationInfo(PbOperationInfo& operation_info, int depth) const
|
||||||
{
|
{
|
||||||
auto operation = CreateOperation(operation_info, ATTACH, "Attach device, device-specific parameters are required");
|
auto operation = CreateOperation(operation_info, ATTACH, "Attach device, device-specific parameters are required");
|
||||||
|
@ -324,6 +382,8 @@ void PiscsiResponse::GetOperationInfo(PbOperationInfo& operation_info, int depth
|
||||||
|
|
||||||
CreateOperation(operation_info, MAPPING_INFO, "Get mapping of extensions to device types");
|
CreateOperation(operation_info, MAPPING_INFO, "Get mapping of extensions to device types");
|
||||||
|
|
||||||
|
CreateOperation(operation_info, STATISTICS_INFO, "Get statistics");
|
||||||
|
|
||||||
CreateOperation(operation_info, RESERVED_IDS_INFO, "Get list of reserved device IDs");
|
CreateOperation(operation_info, RESERVED_IDS_INFO, "Get list of reserved device IDs");
|
||||||
|
|
||||||
operation = CreateOperation(operation_info, DEFAULT_FOLDER, "Set default image file folder");
|
operation = CreateOperation(operation_info, DEFAULT_FOLDER, "Set default image file folder");
|
||||||
|
@ -469,3 +529,8 @@ bool PiscsiResponse::FilterMatches(const string& input, string_view pattern_lowe
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PiscsiResponse::HasOperation(const set<string, less<>>& operations, PbOperation operation)
|
||||||
|
{
|
||||||
|
return operations.empty() || operations.contains(PbOperation_Name(operation));
|
||||||
|
}
|
||||||
|
|
|
@ -11,9 +11,11 @@
|
||||||
|
|
||||||
#include "devices/device_factory.h"
|
#include "devices/device_factory.h"
|
||||||
#include "devices/primary_device.h"
|
#include "devices/primary_device.h"
|
||||||
|
#include "shared/piscsi_util.h"
|
||||||
#include "generated/piscsi_interface.pb.h"
|
#include "generated/piscsi_interface.pb.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace filesystem;
|
using namespace filesystem;
|
||||||
|
@ -33,17 +35,19 @@ public:
|
||||||
void GetDevicesInfo(const unordered_set<shared_ptr<PrimaryDevice>>&, PbResult&, const PbCommand&, const string&) const;
|
void GetDevicesInfo(const unordered_set<shared_ptr<PrimaryDevice>>&, PbResult&, const PbCommand&, const string&) const;
|
||||||
void GetDeviceTypesInfo(PbDeviceTypesInfo&) const;
|
void GetDeviceTypesInfo(PbDeviceTypesInfo&) const;
|
||||||
void GetVersionInfo(PbVersionInfo&) const;
|
void GetVersionInfo(PbVersionInfo&) const;
|
||||||
void GetServerInfo(PbServerInfo&, const unordered_set<shared_ptr<PrimaryDevice>>&, const unordered_set<int>&,
|
void GetServerInfo(PbServerInfo&, const PbCommand&, const unordered_set<shared_ptr<PrimaryDevice>>&,
|
||||||
const string&, const string&, const string&, int) const;
|
const unordered_set<int>&, const string&, int) const;
|
||||||
void GetNetworkInterfacesInfo(PbNetworkInterfacesInfo&) const;
|
void GetNetworkInterfacesInfo(PbNetworkInterfacesInfo&) const;
|
||||||
void GetMappingInfo(PbMappingInfo&) const;
|
void GetMappingInfo(PbMappingInfo&) const;
|
||||||
void GetLogLevelInfo(PbLogLevelInfo&) const;
|
void GetLogLevelInfo(PbLogLevelInfo&) const;
|
||||||
|
void GetStatisticsInfo(PbStatisticsInfo&, const unordered_set<shared_ptr<PrimaryDevice>>&) const;
|
||||||
void GetOperationInfo(PbOperationInfo&, int) const;
|
void GetOperationInfo(PbOperationInfo&, int) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
inline static const vector<string> EMPTY_VECTOR;
|
inline static const vector<string> EMPTY_VECTOR;
|
||||||
|
|
||||||
|
// TODO Try to get rid of this field by having the device instead of the factory providing the device data
|
||||||
const DeviceFactory device_factory;
|
const DeviceFactory device_factory;
|
||||||
|
|
||||||
void GetDeviceProperties(const Device&, PbDeviceProperties&) const;
|
void GetDeviceProperties(const Device&, PbDeviceProperties&) const;
|
||||||
|
@ -59,4 +63,6 @@ private:
|
||||||
static bool ValidateImageFile(const path&);
|
static bool ValidateImageFile(const path&);
|
||||||
|
|
||||||
static bool FilterMatches(const string&, string_view);
|
static bool FilterMatches(const string&, string_view);
|
||||||
|
|
||||||
|
static bool HasOperation(const set<string, less<>>&, PbOperation);
|
||||||
};
|
};
|
||||||
|
|
|
@ -82,9 +82,12 @@ enum PbOperation {
|
||||||
// Make medium writable (not possible for read-only media)
|
// Make medium writable (not possible for read-only media)
|
||||||
UNPROTECT = 9;
|
UNPROTECT = 9;
|
||||||
|
|
||||||
// Gets the server information (PbServerInfo). Calling this operation should be avoided because it
|
// Gets the server information (PbServerInfo). Calling this operation without a list of operations should
|
||||||
// may return a lot of data. More specific other operations should be used instead.
|
// be avoided because this may return a lot of data. More specific other operations should be used instead.
|
||||||
// Parameters:
|
// Parameters:
|
||||||
|
// "operations": Optional case insensitive comma-separated list of operation names to return data for,
|
||||||
|
// e.g. "version_info,log_level_info". Unknown operation names are ignored. If this parameter is missing
|
||||||
|
// the full set of data supported by PbServerInfo is returned.
|
||||||
// "folder_pattern": Optional filter, only folder names containing the case-insensitive pattern are returned
|
// "folder_pattern": Optional filter, only folder names containing the case-insensitive pattern are returned
|
||||||
// "file_pattern": Optional filter, only filenames containing the case-insensitive pattern are returned
|
// "file_pattern": Optional filter, only filenames containing the case-insensitive pattern are returned
|
||||||
SERVER_INFO = 10;
|
SERVER_INFO = 10;
|
||||||
|
@ -190,6 +193,9 @@ enum PbOperation {
|
||||||
|
|
||||||
// Get operation meta data (PbOperationInfo)
|
// Get operation meta data (PbOperationInfo)
|
||||||
OPERATION_INFO = 31;
|
OPERATION_INFO = 31;
|
||||||
|
|
||||||
|
// Get statistics (PbStatisticsInfo)
|
||||||
|
STATISTICS_INFO = 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The operation parameter meta data. The parameter data type is provided by the protobuf API.
|
// The operation parameter meta data. The parameter data type is provided by the protobuf API.
|
||||||
|
@ -286,7 +292,7 @@ message PbImageFile {
|
||||||
string name = 1;
|
string name = 1;
|
||||||
// The assumed device type, based on the filename extension
|
// The assumed device type, based on the filename extension
|
||||||
PbDeviceType type = 2;
|
PbDeviceType type = 2;
|
||||||
// The file size in bytes, 0 for block devices
|
// The file size in bytes, 0 for block devices in /dev
|
||||||
uint64 size = 3;
|
uint64 size = 3;
|
||||||
bool read_only = 4;
|
bool read_only = 4;
|
||||||
}
|
}
|
||||||
|
@ -311,6 +317,41 @@ message PbNetworkInterfacesInfo {
|
||||||
repeated string name = 1;
|
repeated string name = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Statistics categories ordered by increasing severity
|
||||||
|
enum PbStatisticsCategory {
|
||||||
|
CATEGORY_NONE = 0;
|
||||||
|
CATEGORY_INFO = 1;
|
||||||
|
CATEGORY_WARNING = 2;
|
||||||
|
CATEGORY_ERROR = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PbStatistics {
|
||||||
|
PbStatisticsCategory category = 1;
|
||||||
|
// The device ID and LUN for this statistics item. Both are -1 if the item is not device specific.
|
||||||
|
int32 id = 2;
|
||||||
|
int32 unit = 3;
|
||||||
|
// A symbolic unique item name, may be used for I18N. Supported values and their categories:
|
||||||
|
// "read_error_count" (ERROR, SCHD/SCRM/SCMO/SCCD)
|
||||||
|
// "write_error_count" (ERROR, SCHD/SCRM/SCMO)
|
||||||
|
// "cache_miss_read_count" (INFO, SCHD/SCRM/SCMO/SCCD)
|
||||||
|
// "cache_miss_write_count" (INFO, SCHD/SCRM/SCMO)
|
||||||
|
// "sector_read_count" (INFO, SCHD/SCRM/SCMO/SCCD)
|
||||||
|
// "sector_write_count" (INFO, SCHD/SCRM/SCMO)
|
||||||
|
// "byte_read_count" (INFO, SCDP)
|
||||||
|
// "byte_write_count" (INFO, SCDP)
|
||||||
|
// "print_error_count" (ERROR, SCLP)
|
||||||
|
// "print_warning_count" (WARNING, SCLP)
|
||||||
|
// "file_print_count" (INFO, SCLP)
|
||||||
|
// "byte_receive_count" (INFO, SCLP)
|
||||||
|
string key = 4;
|
||||||
|
uint64 value = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The information on collected statistics
|
||||||
|
message PbStatisticsInfo {
|
||||||
|
repeated PbStatistics statistics = 1;
|
||||||
|
}
|
||||||
|
|
||||||
// The device definition, sent from the client to the server
|
// The device definition, sent from the client to the server
|
||||||
message PbDeviceDefinition {
|
message PbDeviceDefinition {
|
||||||
int32 id = 1;
|
int32 id = 1;
|
||||||
|
@ -407,6 +448,8 @@ message PbResult {
|
||||||
PbReservedIdsInfo reserved_ids_info = 12;
|
PbReservedIdsInfo reserved_ids_info = 12;
|
||||||
// The result of an OPERATION_INFO command
|
// The result of an OPERATION_INFO command
|
||||||
PbOperationInfo operation_info = 13;
|
PbOperationInfo operation_info = 13;
|
||||||
|
// The result of a STATISTICS_INFO command
|
||||||
|
PbStatisticsInfo statistics_info = 15;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,4 +473,6 @@ message PbServerInfo {
|
||||||
PbDevicesInfo devices_info = 8;
|
PbDevicesInfo devices_info = 8;
|
||||||
// The operation meta data
|
// The operation meta data
|
||||||
PbOperationInfo operation_info = 9;
|
PbOperationInfo operation_info = 9;
|
||||||
|
// The statistics
|
||||||
|
PbStatisticsInfo statistics_info = 10;
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,9 @@ bool ScsictlCommands::Execute(string_view log_level, string_view default_folder,
|
||||||
case MAPPING_INFO:
|
case MAPPING_INFO:
|
||||||
return CommandMappingInfo();
|
return CommandMappingInfo();
|
||||||
|
|
||||||
|
case STATISTICS_INFO:
|
||||||
|
return CommandStatisticsInfo();
|
||||||
|
|
||||||
case OPERATION_INFO:
|
case OPERATION_INFO:
|
||||||
return CommandOperationInfo();
|
return CommandOperationInfo();
|
||||||
|
|
||||||
|
@ -245,16 +248,43 @@ bool ScsictlCommands::CommandServerInfo()
|
||||||
|
|
||||||
PbServerInfo server_info = result.server_info();
|
PbServerInfo server_info = result.server_info();
|
||||||
|
|
||||||
cout << scsictl_display.DisplayVersionInfo(server_info.version_info());
|
if (server_info.has_version_info()) {
|
||||||
cout << scsictl_display.DisplayLogLevelInfo(server_info.log_level_info());
|
cout << scsictl_display.DisplayVersionInfo(server_info.version_info());
|
||||||
cout << scsictl_display.DisplayImageFilesInfo(server_info.image_files_info());
|
}
|
||||||
cout << scsictl_display.DisplayMappingInfo(server_info.mapping_info());
|
|
||||||
cout << scsictl_display.DisplayNetworkInterfaces(server_info.network_interfaces_info());
|
|
||||||
cout << scsictl_display.DisplayDeviceTypesInfo(server_info.device_types_info());
|
|
||||||
cout << scsictl_display.DisplayReservedIdsInfo(server_info.reserved_ids_info());
|
|
||||||
cout << scsictl_display.DisplayOperationInfo(server_info.operation_info());
|
|
||||||
|
|
||||||
if (server_info.devices_info().devices_size()) {
|
if (server_info.has_log_level_info()) {
|
||||||
|
cout << scsictl_display.DisplayLogLevelInfo(server_info.log_level_info());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server_info.has_image_files_info()) {
|
||||||
|
cout << scsictl_display.DisplayImageFilesInfo(server_info.image_files_info());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server_info.has_mapping_info()) {
|
||||||
|
cout << scsictl_display.DisplayMappingInfo(server_info.mapping_info());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server_info.has_network_interfaces_info()) {
|
||||||
|
cout << scsictl_display.DisplayNetworkInterfaces(server_info.network_interfaces_info());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server_info.has_device_types_info()) {
|
||||||
|
cout << scsictl_display.DisplayDeviceTypesInfo(server_info.device_types_info());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server_info.has_reserved_ids_info()) {
|
||||||
|
cout << scsictl_display.DisplayReservedIdsInfo(server_info.reserved_ids_info());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server_info.has_statistics_info()) {
|
||||||
|
cout << scsictl_display.DisplayStatisticsInfo(server_info.statistics_info());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server_info.has_operation_info()) {
|
||||||
|
cout << scsictl_display.DisplayOperationInfo(server_info.operation_info());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server_info.has_devices_info() && server_info.devices_info().devices_size()) {
|
||||||
vector<PbDevice> sorted_devices = { server_info.devices_info().devices().begin(), server_info.devices_info().devices().end() };
|
vector<PbDevice> sorted_devices = { server_info.devices_info().devices().begin(), server_info.devices_info().devices().end() };
|
||||||
ranges::sort(sorted_devices, [](const auto& a, const auto& b) { return a.id() < b.id() || a.unit() < b.unit(); });
|
ranges::sort(sorted_devices, [](const auto& a, const auto& b) { return a.id() < b.id() || a.unit() < b.unit(); });
|
||||||
|
|
||||||
|
@ -326,6 +356,15 @@ bool ScsictlCommands::CommandMappingInfo()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ScsictlCommands::CommandStatisticsInfo()
|
||||||
|
{
|
||||||
|
SendCommand();
|
||||||
|
|
||||||
|
cout << scsictl_display.DisplayStatisticsInfo(result.statistics_info()) << flush;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool ScsictlCommands::CommandOperationInfo()
|
bool ScsictlCommands::CommandOperationInfo()
|
||||||
{
|
{
|
||||||
SendCommand();
|
SendCommand();
|
||||||
|
|
|
@ -48,6 +48,7 @@ private:
|
||||||
bool CommandLogLevelInfo();
|
bool CommandLogLevelInfo();
|
||||||
bool CommandReservedIdsInfo();
|
bool CommandReservedIdsInfo();
|
||||||
bool CommandMappingInfo();
|
bool CommandMappingInfo();
|
||||||
|
bool CommandStatisticsInfo();
|
||||||
bool CommandOperationInfo();
|
bool CommandOperationInfo();
|
||||||
bool SendCommand();
|
bool SendCommand();
|
||||||
bool EvaluateParams(string_view, const string&, const string&);
|
bool EvaluateParams(string_view, const string&, const string&);
|
||||||
|
|
|
@ -35,7 +35,7 @@ void ScsiCtl::Banner(const vector<char *>& args) const
|
||||||
<< "[-F IMAGE_FOLDER] [-L LOG_LEVEL] [-h HOST] [-p PORT] [-r RESERVED_IDS] "
|
<< "[-F IMAGE_FOLDER] [-L LOG_LEVEL] [-h HOST] [-p PORT] [-r RESERVED_IDS] "
|
||||||
<< "[-C FILENAME:FILESIZE] [-d FILENAME] [-w FILENAME] [-R CURRENT_NAME:NEW_NAME] "
|
<< "[-C FILENAME:FILESIZE] [-d FILENAME] [-w FILENAME] [-R CURRENT_NAME:NEW_NAME] "
|
||||||
<< "[-x CURRENT_NAME:NEW_NAME] [-z LOCALE] "
|
<< "[-x CURRENT_NAME:NEW_NAME] [-z LOCALE] "
|
||||||
<< "[-e] [-E FILENAME] [-D] [-I] [-l] [-m] [o] [-O] [-P] [-s] [-v] [-V] [-y] [-X]\n"
|
<< "[-e] [-E FILENAME] [-D] [-I] [-l] [-m] [o] [-O] [-P] [-s] [-S] [-v] [-V] [-y] [-X]\n"
|
||||||
<< " where ID[:LUN] ID := {0-" << (ControllerManager::GetScsiIdMax() - 1) << "},"
|
<< " where ID[:LUN] ID := {0-" << (ControllerManager::GetScsiIdMax() - 1) << "},"
|
||||||
<< " LUN := {0-" << (ControllerManager::GetScsiLunMax() - 1) << "}, default is 0\n"
|
<< " LUN := {0-" << (ControllerManager::GetScsiLunMax() - 1) << "}, default is 0\n"
|
||||||
<< " CMD := {attach|detach|insert|eject|protect|unprotect|show}\n"
|
<< " CMD := {attach|detach|insert|eject|protect|unprotect|show}\n"
|
||||||
|
@ -82,7 +82,7 @@ int ScsiCtl::run(const vector<char *>& args) const
|
||||||
opterr = 1;
|
opterr = 1;
|
||||||
int opt;
|
int opt;
|
||||||
while ((opt = getopt(static_cast<int>(args.size()), args.data(),
|
while ((opt = getopt(static_cast<int>(args.size()), args.data(),
|
||||||
"e::lmos::vDINOTVXa:b:c:d:f:h:i:n:p:r:t:x:z:C:E:F:L:P::R:")) != -1) {
|
"e::lmos::vDINOSTVXa:b:c:d:f:h:i:n:p:r:t:x:z:C:E:F:L:P::R:")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'i':
|
case 'i':
|
||||||
if (const string error = SetIdAndLun(*device, optarg); !error.empty()) {
|
if (const string error = SetIdAndLun(*device, optarg); !error.empty()) {
|
||||||
|
@ -130,7 +130,7 @@ int ScsiCtl::run(const vector<char *>& args) const
|
||||||
case 'e':
|
case 'e':
|
||||||
command.set_operation(DEFAULT_IMAGE_FILES_INFO);
|
command.set_operation(DEFAULT_IMAGE_FILES_INFO);
|
||||||
if (optarg) {
|
if (optarg) {
|
||||||
SetPatternParams(command, optarg);
|
SetCommandParams(command, optarg);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -208,10 +208,14 @@ int ScsiCtl::run(const vector<char *>& args) const
|
||||||
case 's':
|
case 's':
|
||||||
command.set_operation(SERVER_INFO);
|
command.set_operation(SERVER_INFO);
|
||||||
if (optarg) {
|
if (optarg) {
|
||||||
SetPatternParams(command, optarg);
|
SetCommandParams(command, optarg);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'S':
|
||||||
|
command.set_operation(STATISTICS_INFO);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'v':
|
case 'v':
|
||||||
cout << "scsictl version: " << piscsi_get_version_string() << '\n';
|
cout << "scsictl version: " << piscsi_get_version_string() << '\n';
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
|
|
|
@ -231,6 +231,38 @@ string ScsictlDisplay::DisplayMappingInfo(const PbMappingInfo& mapping_info) con
|
||||||
return s.str();
|
return s.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string ScsictlDisplay::DisplayStatisticsInfo(const PbStatisticsInfo& statistics_info) const
|
||||||
|
{
|
||||||
|
ostringstream s;
|
||||||
|
|
||||||
|
s << "Statistics:\n";
|
||||||
|
|
||||||
|
// Sort by ascending ID, LUN and key and by descending category
|
||||||
|
vector<PbStatistics> sorted_statistics = { statistics_info.statistics().begin(), statistics_info.statistics().end() };
|
||||||
|
ranges::sort(sorted_statistics, [] (const PbStatistics& a, const PbStatistics& b) {
|
||||||
|
if (a.category() > b.category()) return true;
|
||||||
|
if (a.category() < b.category()) return false;
|
||||||
|
if (a.id() < b.id()) return true;
|
||||||
|
if (a.id() > b.id()) return false;
|
||||||
|
if (a.unit() < b.unit()) return true;
|
||||||
|
if (a.unit() > b.unit()) return false;
|
||||||
|
return a.key() < b.key();
|
||||||
|
});
|
||||||
|
|
||||||
|
PbStatisticsCategory prev_category = PbStatisticsCategory::CATEGORY_NONE;
|
||||||
|
for (const auto& statistics : sorted_statistics) {
|
||||||
|
if (statistics.category() != prev_category) {
|
||||||
|
// Strip leading "CATEGORY_"
|
||||||
|
s << " " << PbStatisticsCategory_Name(statistics.category()).substr(9) << '\n';
|
||||||
|
prev_category = statistics.category();
|
||||||
|
}
|
||||||
|
|
||||||
|
s << " " << statistics.id() << ":" << statistics.unit() << " " << statistics.key() << ": " << statistics.value() << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.str();
|
||||||
|
}
|
||||||
|
|
||||||
string ScsictlDisplay::DisplayOperationInfo(const PbOperationInfo& operation_info) const
|
string ScsictlDisplay::DisplayOperationInfo(const PbOperationInfo& operation_info) const
|
||||||
{
|
{
|
||||||
ostringstream s;
|
ostringstream s;
|
||||||
|
|
|
@ -32,6 +32,7 @@ public:
|
||||||
string DisplayImageFilesInfo(const PbImageFilesInfo&) const;
|
string DisplayImageFilesInfo(const PbImageFilesInfo&) const;
|
||||||
string DisplayNetworkInterfaces(const PbNetworkInterfacesInfo&) const;
|
string DisplayNetworkInterfaces(const PbNetworkInterfacesInfo&) const;
|
||||||
string DisplayMappingInfo(const PbMappingInfo&) const;
|
string DisplayMappingInfo(const PbMappingInfo&) const;
|
||||||
|
string DisplayStatisticsInfo(const PbStatisticsInfo&) const;
|
||||||
string DisplayOperationInfo(const PbOperationInfo&) const;
|
string DisplayOperationInfo(const PbOperationInfo&) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -42,21 +42,33 @@ void protobuf_util::ParseParameters(PbDeviceDefinition& device, const string& pa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void protobuf_util::SetPatternParams(PbCommand& command, const string& patterns)
|
void protobuf_util::SetCommandParams(PbCommand& command, const string& params)
|
||||||
{
|
{
|
||||||
string folder_pattern;
|
string folder_pattern;
|
||||||
string file_pattern;
|
string file_pattern;
|
||||||
|
string operations;
|
||||||
|
|
||||||
if (const auto& components = Split(patterns, ':', 2); components.size() == 2) {
|
switch (const auto& components = Split(params, COMPONENT_SEPARATOR, 3); components.size()) {
|
||||||
folder_pattern = components[0];
|
case 3:
|
||||||
file_pattern = components[1];
|
operations = components[2];
|
||||||
}
|
[[fallthrough]];
|
||||||
else {
|
|
||||||
file_pattern = patterns;
|
case 2:
|
||||||
|
folder_pattern = components[0];
|
||||||
|
file_pattern = components[1];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
file_pattern = components[0];
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetParam(command, "folder_pattern", folder_pattern);
|
SetParam(command, "folder_pattern", folder_pattern);
|
||||||
SetParam(command, "file_pattern", file_pattern);
|
SetParam(command, "file_pattern", file_pattern);
|
||||||
|
SetParam(command, "operations", operations);
|
||||||
}
|
}
|
||||||
|
|
||||||
void protobuf_util::SetProductData(PbDeviceDefinition& device, const string& data)
|
void protobuf_util::SetProductData(PbDeviceDefinition& device, const string& data)
|
||||||
|
|
|
@ -38,7 +38,7 @@ namespace protobuf_util
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParseParameters(PbDeviceDefinition&, const string&);
|
void ParseParameters(PbDeviceDefinition&, const string&);
|
||||||
void SetPatternParams(PbCommand&, const string&);
|
void SetCommandParams(PbCommand&, const string&);
|
||||||
void SetProductData(PbDeviceDefinition&, const string&);
|
void SetProductData(PbDeviceDefinition&, const string&);
|
||||||
string SetIdAndLun(PbDeviceDefinition&, const string&);
|
string SetIdAndLun(PbDeviceDefinition&, const string&);
|
||||||
string ListDevices(const vector<PbDevice>&);
|
string ListDevices(const vector<PbDevice>&);
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "mocks.h"
|
#include "mocks.h"
|
||||||
#include "shared/piscsi_version.h"
|
#include "shared/piscsi_version.h"
|
||||||
|
#include "shared/protobuf_util.h"
|
||||||
#include "controllers/controller_manager.h"
|
#include "controllers/controller_manager.h"
|
||||||
#include "devices/device_factory.h"
|
#include "devices/device_factory.h"
|
||||||
#include "generated/piscsi_interface.pb.h"
|
#include "generated/piscsi_interface.pb.h"
|
||||||
|
@ -16,6 +17,7 @@
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
using namespace piscsi_interface;
|
using namespace piscsi_interface;
|
||||||
|
using namespace protobuf_util;
|
||||||
|
|
||||||
TEST(PiscsiResponseTest, Operation_Count)
|
TEST(PiscsiResponseTest, Operation_Count)
|
||||||
{
|
{
|
||||||
|
@ -178,15 +180,41 @@ TEST(PiscsiResponseTest, GetServerInfo)
|
||||||
const unordered_set<shared_ptr<PrimaryDevice>> devices;
|
const unordered_set<shared_ptr<PrimaryDevice>> devices;
|
||||||
const unordered_set<int> ids = { 1, 3 };
|
const unordered_set<int> ids = { 1, 3 };
|
||||||
|
|
||||||
PbServerInfo info;
|
PbCommand command;
|
||||||
response.GetServerInfo(info, devices, ids, "default_folder", "", "", 1234);
|
PbServerInfo info1;
|
||||||
EXPECT_EQ(piscsi_major_version, info.version_info().major_version());
|
response.GetServerInfo(info1, command, devices, ids, "default_folder", 1234);
|
||||||
EXPECT_EQ(piscsi_minor_version, info.version_info().minor_version());
|
EXPECT_TRUE(info1.has_version_info());
|
||||||
EXPECT_EQ(piscsi_patch_version, info.version_info().patch_version());
|
EXPECT_TRUE(info1.has_log_level_info());
|
||||||
EXPECT_EQ(level::level_string_views[get_level()], info.log_level_info().current_log_level());
|
EXPECT_TRUE(info1.has_device_types_info());
|
||||||
EXPECT_EQ("default_folder", info.image_files_info().default_image_folder());
|
EXPECT_TRUE(info1.has_image_files_info());
|
||||||
EXPECT_EQ(1234, info.image_files_info().depth());
|
EXPECT_TRUE(info1.has_network_interfaces_info());
|
||||||
EXPECT_EQ(2, info.reserved_ids_info().ids().size());
|
EXPECT_TRUE(info1.has_mapping_info());
|
||||||
|
EXPECT_TRUE(info1.has_statistics_info());
|
||||||
|
EXPECT_FALSE(info1.has_devices_info());
|
||||||
|
EXPECT_TRUE(info1.has_reserved_ids_info());
|
||||||
|
EXPECT_TRUE(info1.has_operation_info());
|
||||||
|
|
||||||
|
EXPECT_EQ(piscsi_major_version, info1.version_info().major_version());
|
||||||
|
EXPECT_EQ(piscsi_minor_version, info1.version_info().minor_version());
|
||||||
|
EXPECT_EQ(piscsi_patch_version, info1.version_info().patch_version());
|
||||||
|
EXPECT_EQ(level::level_string_views[get_level()], info1.log_level_info().current_log_level());
|
||||||
|
EXPECT_EQ("default_folder", info1.image_files_info().default_image_folder());
|
||||||
|
EXPECT_EQ(1234, info1.image_files_info().depth());
|
||||||
|
EXPECT_EQ(2, info1.reserved_ids_info().ids().size());
|
||||||
|
|
||||||
|
SetParam(command, "operations", "log_level_info,mapping_info");
|
||||||
|
PbServerInfo info2;
|
||||||
|
response.GetServerInfo(info2, command, devices, ids, "default_folder", 1234);
|
||||||
|
EXPECT_FALSE(info2.has_version_info());
|
||||||
|
EXPECT_TRUE(info2.has_log_level_info());
|
||||||
|
EXPECT_FALSE(info2.has_device_types_info());
|
||||||
|
EXPECT_FALSE(info2.has_image_files_info());
|
||||||
|
EXPECT_FALSE(info2.has_network_interfaces_info());
|
||||||
|
EXPECT_TRUE(info2.has_mapping_info());
|
||||||
|
EXPECT_FALSE(info2.has_statistics_info());
|
||||||
|
EXPECT_FALSE(info2.has_devices_info());
|
||||||
|
EXPECT_FALSE(info2.has_reserved_ids_info());
|
||||||
|
EXPECT_FALSE(info2.has_operation_info());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PiscsiResponseTest, GetVersionInfo)
|
TEST(PiscsiResponseTest, GetVersionInfo)
|
||||||
|
|
|
@ -53,27 +53,38 @@ TEST(ProtobufUtil, ParseParameters)
|
||||||
TestSpecialDevice("services");
|
TestSpecialDevice("services");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ProtobufUtil, SetPatternParams)
|
TEST(ProtobufUtil, SetCommandParams)
|
||||||
{
|
{
|
||||||
PbCommand command1;
|
PbCommand command1;
|
||||||
SetPatternParams(command1, "file");
|
SetCommandParams(command1, "file");
|
||||||
EXPECT_EQ("", GetParam(command1, "folder_pattern"));
|
EXPECT_EQ("", GetParam(command1, "folder_pattern"));
|
||||||
EXPECT_EQ("file", GetParam(command1, "file_pattern"));
|
EXPECT_EQ("file", GetParam(command1, "file_pattern"));
|
||||||
|
|
||||||
PbCommand command2;
|
PbCommand command2;
|
||||||
SetPatternParams(command2, ":file");
|
SetCommandParams(command2, ":file");
|
||||||
EXPECT_EQ("", GetParam(command2, "folder_pattern"));
|
EXPECT_EQ("", GetParam(command2, "folder_pattern"));
|
||||||
EXPECT_EQ("file", GetParam(command2, "file_pattern"));
|
EXPECT_EQ("file", GetParam(command2, "file_pattern"));
|
||||||
|
|
||||||
PbCommand command3;
|
PbCommand command3;
|
||||||
SetPatternParams(command3, "folder:");
|
SetCommandParams(command3, "file:");
|
||||||
EXPECT_EQ("folder", GetParam(command3, "folder_pattern"));
|
EXPECT_EQ("file", GetParam(command3, "file_pattern"));
|
||||||
EXPECT_EQ("", GetParam(command3, "file_pattern"));
|
EXPECT_EQ("", GetParam(command3, "folder_pattern"));
|
||||||
|
|
||||||
PbCommand command4;
|
PbCommand command4;
|
||||||
SetPatternParams(command4, "folder:file");
|
SetCommandParams(command4, "folder:file");
|
||||||
EXPECT_EQ("folder", GetParam(command4, "folder_pattern"));
|
EXPECT_EQ("folder", GetParam(command4, "folder_pattern"));
|
||||||
EXPECT_EQ("file", GetParam(command4, "file_pattern"));
|
EXPECT_EQ("file", GetParam(command4, "file_pattern"));
|
||||||
|
|
||||||
|
PbCommand command5;
|
||||||
|
SetCommandParams(command5, "folder:file:");
|
||||||
|
EXPECT_EQ("folder", GetParam(command5, "folder_pattern"));
|
||||||
|
EXPECT_EQ("file", GetParam(command5, "file_pattern"));
|
||||||
|
|
||||||
|
PbCommand command6;
|
||||||
|
SetCommandParams(command6, "folder:file:operations");
|
||||||
|
EXPECT_EQ("folder", GetParam(command6, "folder_pattern"));
|
||||||
|
EXPECT_EQ("file", GetParam(command6, "file_pattern"));
|
||||||
|
EXPECT_EQ("operations", GetParam(command6, "operations"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ProtobufUtil, ListDevices)
|
TEST(ProtobufUtil, ListDevices)
|
||||||
|
|
|
@ -7,30 +7,31 @@ scsictl \- Sends management commands to the piscsi process
|
||||||
\fB\-l\fR |
|
\fB\-l\fR |
|
||||||
\fB\-m\fR |
|
\fB\-m\fR |
|
||||||
\fB\-o\fR |
|
\fB\-o\fR |
|
||||||
\fB\-s\fR |
|
|
||||||
\fB\-v\fR |
|
\fB\-v\fR |
|
||||||
\fB\-D\fR |
|
\fB\-D\fR |
|
||||||
\fB\-I\fR |
|
\fB\-I\fR |
|
||||||
\fB\-L\fR |
|
\fB\-L\fR |
|
||||||
\fB\-O\fR |
|
\fB\-O\fR |
|
||||||
\fB\-P\fR |
|
\fB\-P\fR |
|
||||||
|
\fB\-S\fR |
|
||||||
\fB\-T\fR |
|
\fB\-T\fR |
|
||||||
\fB\-V\fR |
|
\fB\-V\fR |
|
||||||
\fB\-X\fR |
|
\fB\-X\fR |
|
||||||
[\fB\-C\fR \fIFILENAME:FILESIZE\fR]
|
[\fB\-C\fR \fIFILENAME:FILESIZE\fR] |
|
||||||
[\fB\-E\fR \fIFILENAME\fR]
|
[\fB\-E\fR \fIFILENAME\fR] |
|
||||||
[\fB\-F\fR \fIIMAGE_FOLDER\fR]
|
[\fB\-F\fR \fIIMAGE_FOLDER\fR] |
|
||||||
[\fB\-R\fR \fICURRENT_NAME:NEW_NAME\fR]
|
[\fB\-R\fR \fICURRENT_NAME:NEW_NAME\fR] |
|
||||||
[\fB\-c\fR \fICMD\fR]
|
[\fB\-c\fR \fICMD\fR] |
|
||||||
[\fB\-f\fR \fIFILE|PARAM\fR]
|
[\fB\-f\fR \fIFILE|PARAM\fR] |
|
||||||
[\fB\-g\fR \fILOG_LEVEL\fR]
|
[\fB\-g\fR \fILOG_LEVEL\fR] |
|
||||||
[\fB\-h\fR \fIHOST\fR]
|
[\fB\-h\fR \fIHOST\fR] |
|
||||||
[\fB\-i\fR \fIID[:LUN]\fR
|
[\fB\-i\fR \fIID[:LUN]\fR] |
|
||||||
[\fB\-n\fR \fINAME\fR]
|
[\fB\-n\fR \fINAME\fR] |
|
||||||
[\fB\-p\fR \fIPORT\fR]
|
[\fB\-p\fR \fIPORT\fR] |
|
||||||
[\fB\-r\fR \fIRESERVED_IDS\fR]
|
[\fB\-r\fR \fIRESERVED_IDS\fR] |
|
||||||
[\fB\-t\fR \fITYPE\fR]
|
[\fB\-s\fR \fI[FOLDER_PATTERN:FILE_PATTERN:OPERATIONS]\fR] |
|
||||||
[\fB\-x\fR \fICURRENT_NAME:NEW_NAME\fR]
|
[\fB\-t\fR \fITYPE\fR] |
|
||||||
|
[\fB\-x\fR \fICURRENT_NAME:NEW_NAME\fR] |
|
||||||
[\fB\-z\fR \fILOCALE\fR]
|
[\fB\-z\fR \fILOCALE\fR]
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
.B scsictl
|
.B scsictl
|
||||||
|
@ -38,7 +39,7 @@ sends commands to the piscsi process to make configuration adjustments at runtim
|
||||||
|
|
||||||
Either the -i or -l option should be specified at one time. Not both.
|
Either the -i or -l option should be specified at one time. Not both.
|
||||||
|
|
||||||
You do NOT need root privileges to use scsictl.
|
You do NOT need root privileges to use scsictl. scsictl also runs on non-Pi Linux platforms.
|
||||||
|
|
||||||
Note: The command and type arguments are case insensitive. Only the first letter of the command/type is evaluated by the tool.
|
Note: The command and type arguments are case insensitive. Only the first letter of the command/type is evaluated by the tool.
|
||||||
|
|
||||||
|
@ -95,9 +96,12 @@ The piscsi port to connect to, default is 6868.
|
||||||
.BR \-r\fI " " \fIRESERVED_IDS
|
.BR \-r\fI " " \fIRESERVED_IDS
|
||||||
Comma-separated list of IDs to reserve. Pass an empty list in order to not reserve anything.
|
Comma-separated list of IDs to reserve. Pass an empty list in order to not reserve anything.
|
||||||
.TP
|
.TP
|
||||||
.BR \-s\fI
|
.BR \-s\fI " " \fI[FOLDER_PATTERN:FILE_PATTERN:OPERATIONS]
|
||||||
Display server-side settings like available images or supported device types.
|
Display server-side settings like available images or supported device types.
|
||||||
.TP
|
.TP
|
||||||
|
.BR \-S\fI
|
||||||
|
Display statistics.
|
||||||
|
.TP
|
||||||
.BR \-T\fI
|
.BR \-T\fI
|
||||||
Display all device types and their properties.
|
Display all device types and their properties.
|
||||||
.TP
|
.TP
|
||||||
|
|
|
@ -6,19 +6,21 @@ NAME
|
||||||
scsictl - Sends management commands to the piscsi process
|
scsictl - Sends management commands to the piscsi process
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
scsictl -e | -l | -m | -o | -s | -v | -D | -I | -L | -O | -P | -T | -V
|
scsictl -e | -l | -m | -o | -v | -D | -I | -L | -O | -P | -S | -T | -V
|
||||||
| -X | [-C FILENAME:FILESIZE] [-E FILENAME] [-F IMAGE_FOLDER] [-R CUR‐
|
| -X | [-C FILENAME:FILESIZE] | [-E FILENAME] | [-F IMAGE_FOLDER] | [-R
|
||||||
RENT_NAME:NEW_NAME] [-c CMD] [-f FILE|PARAM] [-g LOG_LEVEL] [-h HOST]
|
CURRENT_NAME:NEW_NAME] | [-c CMD] | [-f FILE|PARAM] | [-g LOG_LEVEL] |
|
||||||
[-i ID[:LUN] [-n NAME] [-p PORT] [-r RESERVED_IDS] [-t TYPE] [-x CUR‐
|
[-h HOST] | [-i ID[:LUN]] | [-n NAME] | [-p PORT] | [-r RESERVED_IDS] |
|
||||||
RENT_NAME:NEW_NAME] [-z LOCALE]
|
[-s [FOLDER_PATTERN:FILE_PATTERN:OPERATIONS]] | [-t TYPE] | [-x CUR‐
|
||||||
|
RENT_NAME:NEW_NAME] | [-z LOCALE]
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
scsictl sends commands to the piscsi process to make configuration ad‐
|
scsictl sends commands to the piscsi process to make configuration ad‐
|
||||||
justments at runtime or to check the status of the devices.
|
justments at runtime or to check the status of the devices.
|
||||||
|
|
||||||
Either the -i or -l option should be specified at one time. Not both.
|
Either the -i or -l option should be specified at one time. Not both.
|
||||||
|
|
||||||
You do NOT need root privileges to use scsictl.
|
You do NOT need root privileges to use scsictl. scsictl also runs on
|
||||||
|
non-Pi Linux platforms.
|
||||||
|
|
||||||
Note: The command and type arguments are case insensitive. Only the
|
Note: The command and type arguments are case insensitive. Only the
|
||||||
first letter of the command/type is evaluated by the tool.
|
first letter of the command/type is evaluated by the tool.
|
||||||
|
@ -74,9 +76,12 @@ OPTIONS
|
||||||
Comma-separated list of IDs to reserve. Pass an empty list in
|
Comma-separated list of IDs to reserve. Pass an empty list in
|
||||||
order to not reserve anything.
|
order to not reserve anything.
|
||||||
|
|
||||||
-s Display server-side settings like available images or supported
|
-s [FOLDER_PATTERN:FILE_PATTERN:OPERATIONS]
|
||||||
|
Display server-side settings like available images or supported
|
||||||
device types.
|
device types.
|
||||||
|
|
||||||
|
-S Display statistics.
|
||||||
|
|
||||||
-T Display all device types and their properties.
|
-T Display all device types and their properties.
|
||||||
|
|
||||||
-v Display the piscsi server version.
|
-v Display the piscsi server version.
|
||||||
|
|
Loading…
Reference in New Issue