fixing iscsi mapping for library
This commit is contained in:
@@ -176,6 +176,10 @@ func (a *App) routes() {
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleListVTLMediaChangers(w, r) },
|
||||
nil, nil, nil, nil,
|
||||
))
|
||||
a.mux.HandleFunc("/api/v1/vtl/devices/iscsi", methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleListVTLDevicesForISCSI(w, r) },
|
||||
nil, nil, nil, nil,
|
||||
))
|
||||
a.mux.HandleFunc("/api/v1/vtl/changer/status", methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleGetVTLMediaChangerStatus(w, r) },
|
||||
nil, nil, nil, nil,
|
||||
|
||||
@@ -285,3 +285,42 @@ func (a *App) handleEjectTape(w http.ResponseWriter, r *http.Request) {
|
||||
"drive_id": fmt.Sprintf("%d", req.DriveID),
|
||||
})
|
||||
}
|
||||
|
||||
// handleListVTLDevicesForISCSI returns all tape devices (drives and medium changers) for iSCSI passthrough
|
||||
func (a *App) handleListVTLDevicesForISCSI(w http.ResponseWriter, r *http.Request) {
|
||||
devices := []map[string]interface{}{}
|
||||
|
||||
// Get drives
|
||||
drives, err := a.vtlService.ListDrives()
|
||||
if err == nil {
|
||||
for _, drive := range drives {
|
||||
devices = append(devices, map[string]interface{}{
|
||||
"type": "drive",
|
||||
"device": drive.Device,
|
||||
"id": drive.ID,
|
||||
"library_id": drive.LibraryID,
|
||||
"vendor": drive.Vendor,
|
||||
"product": drive.Product,
|
||||
"description": fmt.Sprintf("Tape Drive %d (Library %d) - %s %s", drive.ID, drive.LibraryID, drive.Vendor, drive.Product),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Get medium changers
|
||||
changers, err := a.vtlService.ListMediaChangers()
|
||||
if err == nil {
|
||||
for _, changer := range changers {
|
||||
devices = append(devices, map[string]interface{}{
|
||||
"type": "changer",
|
||||
"device": changer.Device,
|
||||
"id": changer.ID,
|
||||
"library_id": changer.LibraryID,
|
||||
"slots": changer.Slots,
|
||||
"drives": changer.Drives,
|
||||
"description": fmt.Sprintf("Media Changer (Library %d) - %d slots, %d drives", changer.LibraryID, changer.Slots, changer.Drives),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, devices)
|
||||
}
|
||||
|
||||
@@ -85,21 +85,43 @@
|
||||
|
||||
<!-- Add LUN Modal -->
|
||||
<div id="add-lun-modal" class="hidden fixed inset-0 bg-black/50 flex items-center justify-center z-50">
|
||||
<div class="bg-slate-800 rounded-lg border border-slate-700 p-6 max-w-md w-full mx-4">
|
||||
<div class="bg-slate-800 rounded-lg border border-slate-700 p-6 max-w-md w-full mx-4 max-h-[90vh] overflow-y-auto">
|
||||
<h3 class="text-xl font-semibold text-white mb-4">Add LUN to Target</h3>
|
||||
<form id="add-lun-form" onsubmit="addLUN(event)" class="space-y-4">
|
||||
<input type="hidden" name="target_id" id="lun-target-id">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-300 mb-1">Select Storage Volume</label>
|
||||
<select name="zvol" id="lun-zvol-select" required class="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded text-white text-sm focus:outline-none focus:ring-2 focus:ring-blue-600">
|
||||
<option value="">Loading volumes...</option>
|
||||
</select>
|
||||
<p class="text-xs text-slate-400 mt-1">Or enter manually below</p>
|
||||
<input type="hidden" name="target_type" id="lun-target-type" value="disk">
|
||||
|
||||
<!-- Disk Mode: Storage Volume -->
|
||||
<div id="lun-disk-mode" class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-300 mb-1">Select Storage Volume</label>
|
||||
<select name="zvol" id="lun-zvol-select" class="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded text-white text-sm focus:outline-none focus:ring-2 focus:ring-blue-600">
|
||||
<option value="">Loading volumes...</option>
|
||||
</select>
|
||||
<p class="text-xs text-slate-400 mt-1">Or enter manually below</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-300 mb-1">Or Enter Volume Name Manually</label>
|
||||
<input type="text" name="zvol-manual" id="lun-zvol-manual" placeholder="pool/zvol" class="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded text-white text-sm focus:outline-none focus:ring-2 focus:ring-blue-600">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-300 mb-1">Or Enter Volume Name Manually</label>
|
||||
<input type="text" name="zvol-manual" id="lun-zvol-manual" placeholder="pool/zvol" class="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded text-white text-sm focus:outline-none focus:ring-2 focus:ring-blue-600">
|
||||
|
||||
<!-- Tape Mode: Tape Devices -->
|
||||
<div id="lun-tape-mode" class="space-y-4 hidden">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-300 mb-1">Select Tape Device</label>
|
||||
<select name="device" id="lun-device-select" class="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded text-white text-sm focus:outline-none focus:ring-2 focus:ring-blue-600">
|
||||
<option value="">Loading devices...</option>
|
||||
</select>
|
||||
<p class="text-xs text-slate-400 mt-1">Physical medium changer or tape drive device</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-300 mb-1">Or Enter Device Path Manually</label>
|
||||
<input type="text" name="device-manual" id="lun-device-manual" placeholder="/dev/sg0 or /dev/st0" class="w-full px-3 py-2 bg-slate-900 border border-slate-700 rounded text-white text-sm focus:outline-none focus:ring-2 focus:ring-blue-600">
|
||||
<p class="text-xs text-slate-400 mt-1">Medium changer: /dev/sg* | Tape drive: /dev/st* or /dev/nst*</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 justify-end">
|
||||
<button type="button" onclick="closeModal('add-lun-modal')" class="px-4 py-2 bg-slate-700 hover:bg-slate-600 text-white rounded text-sm">
|
||||
Cancel
|
||||
@@ -212,10 +234,10 @@ async function loadISCSITargets(type = 'disk') {
|
||||
<div class="flex items-center justify-between text-sm">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-slate-400">LUN ${lun.id}:</span>
|
||||
<span class="text-slate-300 font-mono">${lun.zvol}</span>
|
||||
<span class="text-slate-300 font-mono">${lun.zvol || lun.device || 'N/A'}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-slate-300">${formatBytes(lun.size)}</span>
|
||||
${lun.size > 0 ? `<span class="text-slate-300">${formatBytes(lun.size)}</span>` : ''}
|
||||
<button onclick="removeLUN('${target.id}', ${lun.id})" class="ml-2 px-2 py-1 bg-red-600 hover:bg-red-700 text-white rounded text-xs" title="Remove LUN">
|
||||
Remove
|
||||
</button>
|
||||
@@ -281,12 +303,38 @@ function showCreateISCSIModal(type = 'disk') {
|
||||
document.getElementById('create-iscsi-modal').classList.remove('hidden');
|
||||
}
|
||||
|
||||
async function showAddLUNModal(targetId) {
|
||||
async function showAddLUNModal(targetId, targetType) {
|
||||
document.getElementById('lun-target-id').value = targetId;
|
||||
document.getElementById('add-lun-modal').classList.remove('hidden');
|
||||
|
||||
// Load available storage volumes
|
||||
await loadZVOLsForLUN();
|
||||
// Use provided target type, or fallback to current tab
|
||||
const type = targetType || (currentTab === 'tape' ? 'tape' : 'disk');
|
||||
document.getElementById('lun-target-type').value = type;
|
||||
|
||||
// Show/hide appropriate sections
|
||||
const diskMode = document.getElementById('lun-disk-mode');
|
||||
const tapeMode = document.getElementById('lun-tape-mode');
|
||||
const zvolSelect = document.getElementById('lun-zvol-select');
|
||||
const deviceSelect = document.getElementById('lun-device-select');
|
||||
|
||||
if (targetType === 'tape') {
|
||||
diskMode.classList.add('hidden');
|
||||
tapeMode.classList.remove('hidden');
|
||||
zvolSelect.removeAttribute('required');
|
||||
deviceSelect.setAttribute('required', 'required');
|
||||
|
||||
// Load tape devices
|
||||
await loadTapeDevicesForLUN();
|
||||
} else {
|
||||
diskMode.classList.remove('hidden');
|
||||
tapeMode.classList.add('hidden');
|
||||
zvolSelect.setAttribute('required', 'required');
|
||||
deviceSelect.removeAttribute('required');
|
||||
|
||||
// Load storage volumes
|
||||
await loadZVOLsForLUN();
|
||||
}
|
||||
|
||||
document.getElementById('add-lun-modal').classList.remove('hidden');
|
||||
}
|
||||
|
||||
async function loadZVOLsForLUN() {
|
||||
@@ -339,6 +387,56 @@ async function loadZVOLsForLUN() {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadTapeDevicesForLUN() {
|
||||
try {
|
||||
const res = await fetch('/api/v1/vtl/devices/iscsi', { headers: getAuthHeaders() });
|
||||
const selectEl = document.getElementById('lun-device-select');
|
||||
|
||||
if (!res.ok) {
|
||||
selectEl.innerHTML = '<option value="">Error loading devices</option>';
|
||||
return;
|
||||
}
|
||||
|
||||
const devices = await res.json();
|
||||
|
||||
if (!Array.isArray(devices)) {
|
||||
selectEl.innerHTML = '<option value="">No devices available</option>';
|
||||
return;
|
||||
}
|
||||
|
||||
if (devices.length === 0) {
|
||||
selectEl.innerHTML = '<option value="">No tape devices found. Make sure mhvtl is running.</option>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear and populate dropdown
|
||||
selectEl.innerHTML = '<option value="">Select a device...</option>';
|
||||
devices.forEach(device => {
|
||||
const option = document.createElement('option');
|
||||
option.value = device.device;
|
||||
option.textContent = device.description || `${device.type}: ${device.device}`;
|
||||
selectEl.appendChild(option);
|
||||
});
|
||||
|
||||
// Update manual input when dropdown changes
|
||||
selectEl.addEventListener('change', function() {
|
||||
if (this.value) {
|
||||
document.getElementById('lun-device-manual').value = this.value;
|
||||
}
|
||||
});
|
||||
|
||||
// Update dropdown when manual input changes
|
||||
document.getElementById('lun-device-manual').addEventListener('input', function() {
|
||||
if (this.value && !selectEl.value) {
|
||||
// Allow manual entry even if not in dropdown
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Error loading tape devices:', err);
|
||||
document.getElementById('lun-device-select').innerHTML = '<option value="">Error loading devices</option>';
|
||||
}
|
||||
}
|
||||
|
||||
async function showConnectionInstructions(targetId) {
|
||||
try {
|
||||
const res = await fetch(`/api/v1/iscsi/targets/${targetId}/connection`, { headers: getAuthHeaders() });
|
||||
@@ -419,15 +517,27 @@ async function addLUN(e) {
|
||||
let requestBody = {};
|
||||
|
||||
if (targetType === 'tape') {
|
||||
// Tape mode: use device
|
||||
const device = document.getElementById('lun-device-input').value.trim();
|
||||
// Tape mode: use device (medium changer or tape drive)
|
||||
const deviceSelect = document.getElementById('lun-device-select').value;
|
||||
const deviceManual = document.getElementById('lun-device-manual').value.trim();
|
||||
const device = deviceSelect || deviceManual;
|
||||
|
||||
if (!device) {
|
||||
alert('Please enter a tape device path');
|
||||
alert('Please select or enter a tape device path (e.g., /dev/sg0 for medium changer or /dev/st0 for tape drive)');
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine backstore type based on device
|
||||
let backstore = 'pscsi'; // Default for tape devices
|
||||
if (device.startsWith('/dev/sg')) {
|
||||
backstore = 'pscsi'; // Medium changer
|
||||
} else if (device.startsWith('/dev/st') || device.startsWith('/dev/nst')) {
|
||||
backstore = 'pscsi'; // Tape drive
|
||||
}
|
||||
|
||||
requestBody = {
|
||||
device: device,
|
||||
backstore: 'pscsi'
|
||||
backstore: backstore
|
||||
};
|
||||
} else {
|
||||
// Disk mode: use ZVOL
|
||||
@@ -456,7 +566,8 @@ async function addLUN(e) {
|
||||
e.target.reset();
|
||||
document.getElementById('lun-zvol-select').innerHTML = '<option value="">Loading volumes...</option>';
|
||||
document.getElementById('lun-zvol-manual').value = '';
|
||||
document.getElementById('lun-device-input').value = '';
|
||||
document.getElementById('lun-device-select').innerHTML = '<option value="">Loading devices...</option>';
|
||||
document.getElementById('lun-device-manual').value = '';
|
||||
// Reload targets for the current tab
|
||||
loadISCSITargets(targetType);
|
||||
alert('LUN added successfully');
|
||||
|
||||
Reference in New Issue
Block a user