feat: Major VTL System Upgrade (Auth, Monitoring, CLI, Installer)

- Web UI:
  - Added secure Authentication system (Login, 2 Roles: Admin/Viewer)
  - Added System Monitoring Dashboard (Health, Services, Power Mgmt)
  - Added User Management Interface (Create, Delete, Enable/Disable)
  - Added Device Mapping view in iSCSI tab (lsscsi output)
- Backend:
  - Implemented secure session management (auth.php)
  - Added power management APIs (restart/shutdown appliance)
  - Added device mapping API
- CLI:
  - Created global 'vtl' management tool
  - Added scripts for reliable startup (vtllibrary fix)
- Installer:
  - Updated install.sh with new dependencies (tgt, sudoers, permissions)
  - Included all new components in build-installer.sh
- Docs:
  - Consolidated documentation into docs/ folder
This commit is contained in:
2025-12-09 18:15:36 +00:00
parent 8a0523a265
commit 01080498af
53 changed files with 13399 additions and 425 deletions

View File

@@ -1,6 +1,9 @@
<?php
header('Content-Type: application/json');
// Include authentication system
require_once 'auth.php';
// Configuration
$CONFIG_DIR = '/etc/mhvtl';
$DEVICE_CONF = $CONFIG_DIR . '/device.conf';
@@ -21,8 +24,75 @@ if (!$input || !isset($input['action'])) {
$action = $input['action'];
// Public actions (no authentication required)
$publicActions = ['login', 'check_session'];
// Check authentication for non-public actions
if (!in_array($action, $publicActions)) {
requireLogin();
}
switch ($action) {
// Authentication actions
case 'login':
$username = $input['username'] ?? '';
$password = $input['password'] ?? '';
if (authenticateUser($username, $password)) {
echo json_encode([
'success' => true,
'user' => getCurrentUser()
]);
} else {
echo json_encode([
'success' => false,
'error' => 'Invalid username or password'
]);
}
break;
case 'logout':
logout();
echo json_encode(['success' => true]);
break;
case 'check_session':
if (isLoggedIn()) {
echo json_encode([
'success' => true,
'logged_in' => true,
'user' => getCurrentUser()
]);
} else {
echo json_encode([
'success' => true,
'logged_in' => false
]);
}
break;
case 'get_users':
getAllUsers();
break;
case 'create_user':
createUser($input);
break;
case 'update_user':
updateUser($input);
break;
case 'delete_user':
deleteUser($input['username'] ?? '');
break;
case 'change_password':
changePassword($input);
break;
case 'save_config':
requireAdmin(); // Only admin can save config
saveConfig($input['config']);
break;
@@ -31,7 +101,7 @@ switch ($action) {
break;
case 'restart_service':
restartService();
requireAdmin(); // Only admin can restart service
break;
case 'list_tapes':
@@ -74,6 +144,22 @@ switch ($action) {
unbindInitiator($input);
break;
case 'device_mapping':
getDeviceMapping();
break;
case 'system_health':
getSystemHealth();
break;
case 'restart_appliance':
restartAppliance();
break;
case 'shutdown_appliance':
shutdownAppliance();
break;
default:
echo json_encode(['success' => false, 'error' => 'Unknown action']);
}
@@ -652,4 +738,201 @@ function bulkDeleteTapes($pattern) {
]);
}
}
// ============================================
// System Health & Management Functions
// ============================================
function getSystemHealth() {
$health = [];
// Check services
$services = ['mhvtl', 'apache2', 'tgt'];
$health['services'] = [];
foreach ($services as $service) {
$output = [];
$returnCode = 0;
exec("systemctl is-active $service 2>&1", $output, $returnCode);
$isActive = ($returnCode === 0 && trim($output[0]) === 'active');
// Get enabled status
$enabledOutput = [];
$enabledCode = 0;
exec("systemctl is-enabled $service 2>&1", $enabledOutput, $enabledCode);
$isEnabled = ($enabledCode === 0 && trim($enabledOutput[0]) === 'enabled');
$health['services'][$service] = [
'running' => $isActive,
'enabled' => $isEnabled,
'status' => $isActive ? 'running' : 'stopped'
];
}
// Check components
$health['components'] = [];
// vtltape processes
$output = [];
exec("pgrep -f 'vtltape' | wc -l", $output);
$vtltapeCount = intval($output[0]);
$health['components']['vtltape'] = [
'running' => $vtltapeCount > 0,
'count' => $vtltapeCount
];
// vtllibrary process
$output = [];
$returnCode = 0;
exec("pgrep -f 'vtllibrary' 2>&1", $output, $returnCode);
$health['components']['vtllibrary'] = [
'running' => $returnCode === 0
];
// Check SCSI devices
$health['devices'] = [];
// Library
$output = [];
exec("lsscsi -g 2>/dev/null | grep mediumx", $output);
$health['devices']['library'] = [
'detected' => count($output) > 0,
'info' => count($output) > 0 ? $output[0] : null
];
// Tape drives
$output = [];
exec("lsscsi -g 2>/dev/null | grep tape", $output);
$health['devices']['drives'] = [
'detected' => count($output) > 0,
'count' => count($output),
'list' => $output
];
// Calculate overall health score
$totalChecks = 0;
$passedChecks = 0;
foreach ($health['services'] as $service) {
$totalChecks++;
if ($service['running']) $passedChecks++;
}
if ($health['components']['vtltape']['running']) $passedChecks++;
$totalChecks++;
if ($health['components']['vtllibrary']['running']) $passedChecks++;
$totalChecks++;
if ($health['devices']['library']['detected']) $passedChecks++;
$totalChecks++;
if ($health['devices']['drives']['detected']) $passedChecks++;
$totalChecks++;
$percentage = $totalChecks > 0 ? round(($passedChecks / $totalChecks) * 100) : 0;
if ($percentage == 100) {
$status = 'healthy';
$message = 'All systems operational';
} elseif ($percentage >= 66) {
$status = 'degraded';
$message = 'Some components need attention';
} else {
$status = 'critical';
$message = 'Multiple components offline';
}
$health['overall'] = [
'status' => $status,
'message' => $message,
'score' => $passedChecks,
'total' => $totalChecks,
'percentage' => $percentage
];
// System info
$output = [];
exec("uptime -p 2>/dev/null || uptime", $output);
$health['system'] = [
'uptime' => isset($output[0]) ? $output[0] : 'Unknown'
];
echo json_encode([
'success' => true,
'health' => $health
]);
}
function restartAppliance() {
// Create a script to restart after a delay
$script = '#!/bin/bash
sleep 2
systemctl reboot
';
$scriptPath = '/tmp/restart-appliance.sh';
file_put_contents($scriptPath, $script);
chmod($scriptPath, 0755);
// Execute in background
exec("sudo $scriptPath > /dev/null 2>&1 &");
echo json_encode([
'success' => true,
'message' => 'System restart initiated. The appliance will reboot in a few seconds.'
]);
}
function shutdownAppliance() {
// Create a script to shutdown after a delay
$script = '#!/bin/bash
sleep 2
systemctl poweroff
';
$scriptPath = '/tmp/shutdown-appliance.sh';
file_put_contents($scriptPath, $script);
chmod($scriptPath, 0755);
// Execute in background
exec("sudo $scriptPath > /dev/null 2>&1 &");
echo json_encode([
'success' => true,
'message' => 'System shutdown initiated. The appliance will power off in a few seconds.'
]);
}
function getDeviceMapping() {
$output = [];
// Get all SCSI devices with generic device names (sg)
exec("lsscsi -g 2>&1", $output);
// Filter for interesting devices (mediumx and tape)
$devices = [];
foreach ($output as $line) {
// Parse the line to make it cleaner if needed, or just return raw lines
// Example line: [3:0:0:0] mediumx ADASTRA HEPHAESTUS-V 0107 - /dev/sg6
if (strpos($line, 'mediumx') !== false || strpos($line, 'tape') !== false) {
$parts = preg_split('/\s+/', $line);
$devices[] = [
'raw' => $line,
'scsi_id' => $parts[0] ?? '',
'type' => $parts[1] ?? '',
'vendor' => $parts[2] ?? '',
'model' => $parts[3] . (isset($parts[4]) && $parts[4] != '-' && !str_starts_with($parts[4], '/dev') ? ' ' . $parts[4] : '') ?? '',
'rev' => '', // specific parsing depends on varying output
'dev_path' => end($parts) // typically the last one is /dev/sgX
];
}
}
echo json_encode([
'success' => true,
'devices' => $devices,
'raw_output' => $output
]);
}
?>