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:
285
web-ui/api.php
285
web-ui/api.php
@@ -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
|
||||
]);
|
||||
}
|
||||
?>
|
||||
|
||||
Reference in New Issue
Block a user