@oletizi/audio-tools
Version:
Monorepo for hardware sampler utilities and format parsers
137 lines (124 loc) • 4.5 kB
text/typescript
import path from "pathe";
import { AkaiToolsConfig, RemoteDisk, RemoteVolumeResult } from "@/index.js";
import { ExecutionResult } from '@/io/akaitools-core.js';
import { doSpawn } from '@/io/akaitools-process.js';
/**
* Parse remote volumes output from scsictl
* @param data Raw output from scsictl -l command
* @returns Array of remote disks with SCSI ID and image path
*/
export function parseRemoteVolumes(data: string): RemoteDisk[] {
const rv: RemoteDisk[] = [];
data.split('\n').forEach(i => {
const match = i.match(/\|\s*(\d+).*/);
if (match) {
rv.push({
scsiId: Number.parseInt(i.substring(2, 4).trim()),
lun: Number.parseInt(i.substring(7, 12).trim()),
image: i.substring(14).trim()
});
}
});
return rv;
}
/**
* List remote volumes on PiSCSI
* @param c Configuration with piscsiHost
* @returns Promise resolving to list of remote volumes
*/
export async function remoteVolumes(c: AkaiToolsConfig): Promise<RemoteVolumeResult> {
const rv: RemoteVolumeResult = { data: [], errors: [] };
if (!c.piscsiHost) {
rv.errors.push(new Error('Piscsi host is not defined.'));
} else {
await doSpawn('ssh', [c.piscsiHost, '"scsictl -l"'], {
onData: data => {
rv.data = rv.data.concat(parseRemoteVolumes(String(data)));
},
onStart: () => {
// No action needed on start
}
});
}
return rv;
}
/**
* Unmount a remote volume on PiSCSI
* @param c Configuration with piscsiHost
* @param v Remote disk to unmount
* @returns Promise resolving to execution result
*/
export async function remoteUnmount(c: AkaiToolsConfig, v: RemoteDisk): Promise<ExecutionResult> {
const rv: ExecutionResult = { code: -1, errors: [] };
if (!c.piscsiHost) {
rv.errors.push(new Error('Piscsi host is not defined.'));
} else {
const result = await doSpawn('ssh', [c.piscsiHost, `"scsictl -c d -i ${v.scsiId}"`]);
rv.code = result.code;
rv.errors = rv.errors.concat(result.errors);
}
return rv;
}
/**
* Mount a remote volume on PiSCSI
* @param c Configuration with piscsiHost
* @param v Remote disk to mount
* @returns Promise resolving to execution result
*/
export async function remoteMount(c: AkaiToolsConfig, v: RemoteDisk): Promise<ExecutionResult> {
const rv: ExecutionResult = { code: -1, errors: [] };
if (!c.piscsiHost) {
rv.errors.push(new Error('Piscsi host is not defined'));
} else {
const result = await doSpawn('ssh', [c.piscsiHost, `"scsictl -c a -i ${v.scsiId} -f ${v.image}"`]);
rv.code = result.code;
rv.errors = rv.errors.concat(result.errors);
}
return rv;
}
/**
* Synchronize local disk image to remote PiSCSI and mount it
* @param c Configuration with piscsiHost, scsiId, and diskFile
* @returns Promise resolving to execution result
*/
export async function remoteSync(c: AkaiToolsConfig): Promise<ExecutionResult> {
const rv: ExecutionResult = { code: -1, errors: [] };
if (!c.piscsiHost || c.scsiId === undefined) {
rv.errors.push(new Error('Remote host not defined.'));
return rv;
}
console.log(`Listing remote volumes...`);
const result = await remoteVolumes(c);
rv.errors = rv.errors.concat(result.errors);
if (result.errors.length !== 0) {
return rv;
}
let targetVolume: RemoteDisk | undefined;
for (const v of result.data) {
if (v.scsiId === c.scsiId) {
targetVolume = v;
break;
}
}
const parsedPath = path.parse(c.diskFile);
const imagePath = `"~/images/${parsedPath.name}${parsedPath.ext}"`;
if (targetVolume) {
const r = await remoteUnmount(c, targetVolume);
if (r.errors.length !== 0) {
rv.errors = rv.errors.concat(r.errors);
return rv;
}
}
targetVolume = { image: imagePath, scsiId: c.scsiId };
const syncResult = await doSpawn('scp', [`"${c.diskFile}"`, `${c.piscsiHost}:${targetVolume.image}`]);
if (syncResult.errors.length !== 0) {
rv.errors = rv.errors.concat(syncResult.errors);
}
const mountResult = await remoteMount(c, targetVolume);
if (mountResult.errors.length !== 0) {
rv.errors = rv.errors.concat(mountResult.errors);
return rv;
}
rv.code = rv.errors.length;
return rv;
}