Files
vtl-appliance/web-ui/index.html
Othman H. Suseno 01080498af 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
2025-12-09 18:15:36 +00:00

610 lines
29 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>mhvtl Configuration Manager - Adastra VTL</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<nav class="navbar">
<div class="container">
<div class="nav-brand">
<h1>🎞️ Adastra VTL</h1>
<span class="subtitle">Virtual Tape Library Configuration</span>
</div>
<div class="nav-links">
<a href="#system" class="nav-link">System</a>
<a href="#library" class="nav-link active">Library</a>
<a href="#drives" class="nav-link">Drives</a>
<a href="#tapes" class="nav-link">Tapes</a>
<a href="#manage-tapes" class="nav-link">Manage Tapes</a>
<a href="#iscsi" class="nav-link">iSCSI</a>
<a href="#users" class="nav-link" id="users-tab">Users</a>
<a href="#export" class="nav-link">Export</a>
</div>
</div>
</nav>
<main class="container">
<section id="system" class="section">
<div class="section-header">
<h2>🖥️ System Monitoring & Management</h2>
<p>Monitor system health and manage appliance power</p>
</div>
<div class="card">
<div class="card-header">
<h3>💚 System Health Dashboard</h3>
<button class="btn btn-primary" onclick="refreshSystemHealth()">
<span>🔄</span> Refresh
</button>
</div>
<div class="card-body">
<div id="health-loading" style="display: none; text-align: center; padding: 2rem;">
<strong></strong> Loading system health...
</div>
<div id="health-dashboard">
<!-- Health dashboard will be populated here -->
</div>
</div>
</div>
<div class="card" style="margin-top: 1rem;">
<div class="card-header">
<h3>⚡ Power Management</h3>
</div>
<div class="card-body">
<p><strong>⚠️ Warning:</strong> These actions will affect the entire appliance.</p>
<div style="margin-top: 1rem;">
<button class="btn btn-warning" onclick="restartAppliance()" style="margin-right: 1rem;">
<span>🔄</span> Restart Appliance
</button>
<button class="btn btn-danger" onclick="shutdownAppliance()">
<span></span> Shutdown Appliance
</button>
</div>
<div id="power-result" class="alert" style="display: none; margin-top: 1rem;"></div>
</div>
</div>
</section>
<section id="library" class="section active">
<div class="section-header">
<h2>📚 Library Configuration</h2>
<p>Configure your virtual tape library settings</p>
</div>
<div class="card">
<div class="card-header">
<h3>Library Settings</h3>
</div>
<div class="card-body">
<div class="form-grid">
<div class="form-group">
<label for="lib-id">Library ID</label>
<input type="number" id="lib-id" value="10" min="0" max="99">
</div>
<div class="form-group">
<label for="lib-channel">Channel</label>
<input type="number" id="lib-channel" value="0" min="0" max="15">
</div>
<div class="form-group">
<label for="lib-target">Target</label>
<input type="number" id="lib-target" value="0" min="0" max="15">
</div>
<div class="form-group">
<label for="lib-lun">LUN</label>
<input type="number" id="lib-lun" value="0" min="0" max="7">
</div>
<div class="form-group">
<label for="lib-vendor">Vendor</label>
<input type="text" id="lib-vendor" value="STK" maxlength="8">
</div>
<div class="form-group">
<label for="lib-product">Product</label>
<input type="text" id="lib-product" value="L700" maxlength="16">
</div>
<div class="form-group">
<label for="lib-serial">Serial Number</label>
<input type="text" id="lib-serial" value="XYZZY_A" maxlength="10">
</div>
<div class="form-group">
<label for="lib-naa">NAA</label>
<input type="text" id="lib-naa" value="10:22:33:44:ab:cd:ef:00" pattern="[0-9a-f:]+">
</div>
<div class="form-group">
<label for="lib-home">Home Directory</label>
<input type="text" id="lib-home" value="/opt/mhvtl">
</div>
<div class="form-group">
<label for="lib-backoff">Backoff (ms)</label>
<input type="number" id="lib-backoff" value="400" min="0" max="10000">
</div>
</div>
</div>
</div>
</section>
<section id="drives" class="section">
<div class="section-header">
<h2>💾 Drive Configuration</h2>
<p>Manage virtual tape drives</p>
</div>
<div class="drives-container" id="drives-container">
</div>
<button class="btn btn-primary" onclick="addDrive()">
<span></span> Add Drive
</button>
</section>
<section id="tapes" class="section">
<div class="section-header">
<h2>📼 Tape Configuration</h2>
<p>Configure virtual tape media</p>
</div>
<div class="card">
<div class="card-header">
<h3>Tape Generation Settings</h3>
</div>
<div class="card-body">
<div class="form-grid">
<div class="form-group">
<label for="tape-library">Library ID</label>
<input type="number" id="tape-library" value="10" min="0" max="99">
</div>
<div class="form-group">
<label for="tape-barcode-prefix">Barcode Prefix</label>
<input type="text" id="tape-barcode-prefix" value="CLN" maxlength="6">
</div>
<div class="form-group">
<label for="tape-start-num">Starting Number</label>
<input type="number" id="tape-start-num" value="100" min="1" max="9999">
</div>
<div class="form-group">
<label for="tape-size">Tape Size (MB)</label>
<input type="number" id="tape-size" value="2500000" min="1000" max="10000000">
</div>
<div class="form-group">
<label for="tape-media-type">Media Type</label>
<select id="tape-media-type">
<option value="data">Data</option>
<option value="clean">Cleaning</option>
<option value="WORM">WORM</option>
</select>
</div>
<div class="form-group">
<label for="tape-density">Density</label>
<select id="tape-density">
<option value="LTO5">LTO-5</option>
<option value="LTO6" selected>LTO-6</option>
<option value="LTO7">LTO-7</option>
<option value="LTO8">LTO-8</option>
<option value="LTO9">LTO-9</option>
</select>
</div>
<div class="form-group">
<label for="tape-count">Number of Tapes</label>
<input type="number" id="tape-count" value="20" min="1" max="1000">
</div>
</div>
<div class="alert alert-info">
<strong> Info:</strong> Generate mktape commands for creating virtual tapes. Run these
commands on the server after installation.
</div>
</div>
</div>
</section>
<section id="manage-tapes" class="section">
<div class="section-header">
<h2>🗂️ Manage Virtual Tapes</h2>
<p>Complete CRUD management for virtual tape files</p>
</div>
<div class="card">
<div class="card-header">
<h3> Create New Tapes</h3>
</div>
<div class="card-body">
<div class="form-grid">
<div class="form-group">
<label for="create-library">Library Number</label>
<input type="number" id="create-library" value="10" min="1">
</div>
<div class="form-group">
<label for="create-barcode-prefix">Barcode Prefix</label>
<input type="text" id="create-barcode-prefix" value="CLN" maxlength="6">
</div>
<div class="form-group">
<label for="create-start-num">Starting Number</label>
<input type="number" id="create-start-num" value="100" min="0">
</div>
<div class="form-group">
<label for="create-count">Number of Tapes</label>
<input type="number" id="create-count" value="1" min="1" max="100">
</div>
<div class="form-group">
<label for="create-size">Tape Size (MB)</label>
<input type="number" id="create-size" value="2500000" min="1000">
<small>Default: 2.5TB = 2,500,000 MB</small>
</div>
<div class="form-group">
<label for="create-media-type">Media Type</label>
<select id="create-media-type">
<option value="data">Data</option>
<option value="clean">Cleaning</option>
<option value="WORM">WORM</option>
</select>
</div>
<div class="form-group">
<label for="create-density">Density</label>
<select id="create-density">
<option value="LTO5">LTO-5</option>
<option value="LTO6" selected>LTO-6</option>
<option value="LTO7">LTO-7</option>
<option value="LTO8">LTO-8</option>
<option value="LTO9">LTO-9</option>
</select>
</div>
</div>
<div style="margin-top: 1rem;">
<button class="btn btn-success" onclick="createTapes()">
<span></span> Create Tapes
</button>
<div id="create-result" class="alert" style="display: none; margin-top: 1rem;"></div>
</div>
</div>
</div>
<div class="card" style="margin-top: 1rem;">
<div class="card-header">
<h3>📋 Tape Files</h3>
<button class="btn btn-primary" onclick="loadTapeList()">
<span>🔄</span> Refresh List
</button>
</div>
<div class="card-body">
<div id="tape-list-loading" style="display: none; text-align: center; padding: 2rem;">
<strong></strong> Loading tape files...
</div>
<div id="tape-list-error" class="alert alert-danger" style="display: none;"></div>
<div id="tape-list-empty" class="alert alert-info" style="display: none;">
<strong></strong> No tape files found. Create tapes using the commands from the "Tapes"
section.
</div>
<div id="tape-list-container" style="display: none;">
<div style="margin-bottom: 1rem;">
<input type="text" id="tape-search" placeholder="🔍 Search tapes..."
style="width: 100%; padding: 0.5rem; border: 1px solid #ddd; border-radius: 4px;"
onkeyup="filterTapes()">
</div>
<table class="tape-table" id="tape-table">
<thead>
<tr>
<th>Barcode</th>
<th>Size</th>
<th>Modified</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="tape-list-body">
</tbody>
</table>
<div style="margin-top: 1rem; padding: 1rem; background: #f8f9fa; border-radius: 4px;">
<strong>Total Tapes:</strong> <span id="tape-count-display">0</span>
</div>
</div>
</div>
</div>
<div class="card" style="margin-top: 1rem;">
<div class="card-header">
<h3>Bulk Actions</h3>
</div>
<div class="card-body">
<p>Delete multiple tapes at once. Use with caution!</p>
<div class="form-group">
<label for="bulk-delete-pattern">Delete Pattern (e.g., CLN*)</label>
<input type="text" id="bulk-delete-pattern" placeholder="CLN*">
</div>
<button class="btn btn-danger" onclick="bulkDeleteTapes()">
<span>🗑️</span> Bulk Delete
</button>
<div id="bulk-delete-result" class="alert" style="display: none; margin-top: 1rem;"></div>
</div>
</div>
</section>
<section id="iscsi" class="section">
<div class="section-header">
<h2>🔌 iSCSI Target Management</h2>
<p>Manage iSCSI targets, initiators, and LUNs</p>
</div>
<div class="card">
<div class="card-header">
<h3>💾 Device Mapping</h3>
<button class="btn btn-primary" onclick="loadDeviceMapping()">
<span>🔄</span> Refresh
</button>
</div>
<div class="card-body">
<p>Current SCSI devices available for iSCSI export:</p>
<div id="device-mapping-loading" style="display: none; text-align: center; padding: 2rem;">
<strong></strong> Loading device mapping...
</div>
<div id="device-mapping">
<!-- Device mapping will be populated here -->
</div>
</div>
</div>
<div class="card" style="margin-top: 1rem;">
<div class="card-header">
<h3>🎯 iSCSI Targets</h3>
<button class="btn btn-primary" onclick="loadTargets()">
<span>🔄</span> Refresh
</button>
</div>
<div class="card-body">
<div id="target-list-empty" class="alert alert-info">
<strong></strong> No targets configured. Create a target below.
</div>
<div id="target-list-container" style="display: none;">
<table class="tape-table" id="target-table">
<thead>
<tr>
<th>TID</th>
<th>Target Name (IQN)</th>
<th>LUNs</th>
<th>ACLs</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="target-list-body">
</tbody>
</table>
</div>
</div>
</div>
<div class="card" style="margin-top: 1rem;">
<div class="card-header">
<h3> Create New Target</h3>
</div>
<div class="card-body">
<div class="form-grid">
<div class="form-group">
<label for="target-tid">Target ID (TID)</label>
<input type="number" id="target-tid" value="1" min="1">
<small>Unique target identifier</small>
</div>
<div class="form-group">
<label for="target-name">Target Name</label>
<input type="text" id="target-name" placeholder="vtl.drive0">
<small>Will be: iqn.2024-01.com.vtl-linux:<name></small>
</div>
</div>
<button class="btn btn-success" onclick="createTarget()">
<span></span> Create Target
</button>
<div id="create-target-result" class="alert" style="display: none; margin-top: 1rem;"></div>
</div>
</div>
<div class="card" style="margin-top: 1rem;">
<div class="card-header">
<h3>💾 Add LUN (Backing Store)</h3>
</div>
<div class="card-body">
<div class="form-grid">
<div class="form-group">
<label for="lun-tid">Target ID</label>
<input type="number" id="lun-tid" value="1" min="1">
</div>
<div class="form-group">
<label for="lun-number">LUN Number</label>
<input type="number" id="lun-number" value="1" min="0">
</div>
<div class="form-group">
<label for="lun-device">Device Path</label>
<input type="text" id="lun-device" placeholder="/dev/sg1">
<small>SCSI generic device (e.g., /dev/sg1, /dev/sg2)</small>
</div>
</div>
<button class="btn btn-success" onclick="addLun()">
<span></span> Add LUN
</button>
<div id="add-lun-result" class="alert" style="display: none; margin-top: 1rem;"></div>
</div>
</div>
<div class="card" style="margin-top: 1rem;">
<div class="card-header">
<h3>🔐 Manage Initiator ACLs</h3>
</div>
<div class="card-body">
<div class="form-grid">
<div class="form-group">
<label for="acl-tid">Target ID</label>
<input type="number" id="acl-tid" value="1" min="1">
</div>
<div class="form-group">
<label for="acl-address">Initiator Address</label>
<input type="text" id="acl-address" placeholder="192.168.1.100 or ALL">
<small>IP address or "ALL" for any initiator</small>
</div>
</div>
<div style="margin-top: 1rem;">
<button class="btn btn-success" onclick="bindInitiator()">
<span></span> Allow Initiator
</button>
<button class="btn btn-danger" onclick="unbindInitiator()">
<span>🚫</span> Block Initiator
</button>
</div>
<div id="acl-result" class="alert" style="display: none; margin-top: 1rem;"></div>
</div>
</div>
</section>
<section id="users" class="section">
<div class="section-header">
<h2>👥 User Management</h2>
<p>Manage user accounts and permissions (Admin Only)</p>
</div>
<div class="card">
<div class="card-header">
<h3>📋 User List</h3>
<button class="btn btn-primary" onclick="loadUsers()">
<span>🔄</span> Refresh
</button>
</div>
<div class="card-body">
<div id="users-loading" style="display: none; text-align: center; padding: 2rem;">
<strong></strong> Loading users...
</div>
<div id="users-list">
<!-- Users will be populated here -->
</div>
</div>
</div>
<div class="card" style="margin-top: 1rem;">
<div class="card-header">
<h3> Create New User</h3>
</div>
<div class="card-body">
<div class="form-grid">
<div class="form-group">
<label for="new-username">Username</label>
<input type="text" id="new-username" placeholder="Enter username" required>
</div>
<div class="form-group">
<label for="new-password">Password</label>
<input type="password" id="new-password" placeholder="Enter password" required>
</div>
<div class="form-group">
<label for="new-role">Role</label>
<select id="new-role">
<option value="viewer">Viewer (Read-Only)</option>
<option value="admin">Admin (Full Access)</option>
</select>
</div>
</div>
<div style="margin-top: 1rem;">
<button class="btn btn-success" onclick="createNewUser()">
<span></span> Create User
</button>
</div>
<div id="create-user-result" class="alert" style="display: none; margin-top: 1rem;"></div>
</div>
</div>
<div class="card" style="margin-top: 1rem;">
<div class="card-header">
<h3>🔑 Change Password</h3>
</div>
<div class="card-body">
<p>Change your own password</p>
<div class="form-grid">
<div class="form-group">
<label for="current-password">Current Password</label>
<input type="password" id="current-password" required>
</div>
<div class="form-group">
<label for="new-user-password">New Password</label>
<input type="password" id="new-user-password" required>
</div>
<div class="form-group">
<label for="confirm-password">Confirm New Password</label>
<input type="password" id="confirm-password" required>
</div>
</div>
<div style="margin-top: 1rem;">
<button class="btn btn-warning" onclick="changeUserPassword()">
<span>🔑</span> Change Password
</button>
</div>
<div id="change-password-result" class="alert" style="display: none; margin-top: 1rem;"></div>
</div>
</div>
</section>
<section id="export" class="section">
<div class="section-header">
<h2>📤 Export Configuration</h2>
<p>Generate and download configuration files</p>
</div>
<div class="card">
<div class="card-header">
<h3>Configuration Preview</h3>
</div>
<div class="card-body">
<pre id="config-preview" class="config-preview"></pre>
</div>
</div>
<div class="button-group">
<button class="btn btn-primary" onclick="generateConfig()">
<span>🔄</span> Generate Config
</button>
<button class="btn btn-success" onclick="applyConfig()">
<span>💾</span> Apply to Server
</button>
<button class="btn btn-success" onclick="downloadConfig()">
<span>⬇️</span> Download device.conf
</button>
<button class="btn btn-secondary" onclick="copyConfig()">
<span>📋</span> Copy to Clipboard
</button>
</div>
<div id="apply-result" class="alert" style="display: none; margin-top: 1rem;"></div>
<div class="card" style="margin-top: 1rem;">
<div class="card-header">
<h3>Service Management</h3>
</div>
<div class="card-body">
<p>After applying configuration, restart the mhvtl service to apply changes.</p>
<button class="btn btn-warning" onclick="restartService()">
<span>🔄</span> Restart mhvtl Service
</button>
<div id="restart-result" class="alert" style="display: none; margin-top: 1rem;"></div>
</div>
</div>
<div class="card">
<div class="card-header">
<h3>Installation Command</h3>
</div>
<div class="card-body">
<pre id="install-command" class="config-preview"></pre>
<button class="btn btn-secondary" onclick="copyInstallCommand()">
<span>📋</span> Copy Command
</button>
</div>
</div>
</section>
</main>
<footer class="footer">
<div class="container">
<p>© Adastra Visi Teknologi • <a href="http://adastra.id">adastra.id</a> • mhvtl Configuration Manager</p>
</div>
</footer>
<script src="script.js"></script>
</body>
</html>