diff --git a/CHANGELOG.md b/CHANGELOG.md index cb13e87585..380e149d92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Guests can now choose to remember their name for future video conferences ([#2450], [#3275]) - Room access is now preserved across page reloads after entering via an access code or personalized link ([#3275]) - Login button inside room access overlay to allow users to log in instead of accessing the room as a guest ([#2450], [#3275]) +- Option to set connection status of servers to always online ([#3317], [#3373]) ### Changed @@ -25,6 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Guest name input was moved from join dialog to room access overlay ([#2450], [#3275]) - Room share link now includes the access code, so users no longer need to enter it manually when opening the link ([#3275]) - Improved fallback behavior for invalid room and user tab links ([#3275]) +- Prometheus metric label `pilos_servers_total{status="unhealthy"}` to `pilos_servers_total{status="faulty"}` ([#3373]) +- Connection status terminology in log messages (`unhealthy` to `faulty`; `healthy` to `online`; `old_health` to `old_connection_status`) ([#3373]) ### Fixed @@ -896,6 +899,8 @@ You can find the changelog for older versions there [here](https://github.com/TH [#3314]: https://github.com/THM-Health/PILOS/issues/3314 [#3315]: https://github.com/THM-Health/PILOS/pull/3315 [#3316]: https://github.com/THM-Health/PILOS/pull/3316 +[#3317]: https://github.com/THM-Health/PILOS/issues/3317 +[#3373]: https://github.com/THM-Health/PILOS/pull/3373 [unreleased]: https://github.com/THM-Health/PILOS/compare/v4.17.0...develop [v3.0.0]: https://github.com/THM-Health/PILOS/releases/tag/v3.0.0 [v3.0.1]: https://github.com/THM-Health/PILOS/releases/tag/v3.0.1 diff --git a/app/Enums/ServerHealth.php b/app/Enums/ServerConnectionStatus.php similarity index 51% rename from app/Enums/ServerHealth.php rename to app/Enums/ServerConnectionStatus.php index 25eb470f2b..f7b3c3595f 100644 --- a/app/Enums/ServerHealth.php +++ b/app/Enums/ServerConnectionStatus.php @@ -4,12 +4,9 @@ namespace App\Enums; -/** - * Custom status response codes of the api - */ -enum ServerHealth: int +enum ServerConnectionStatus: int { case ONLINE = 1; - case UNHEALTHY = 0; + case FAULTY = 0; case OFFLINE = -1; } diff --git a/app/Enums/ServerStatus.php b/app/Enums/ServerStatus.php index 63b29a3e31..36301c493a 100644 --- a/app/Enums/ServerStatus.php +++ b/app/Enums/ServerStatus.php @@ -4,9 +4,6 @@ namespace App\Enums; -/** - * Custom status response codes of the api - */ enum ServerStatus: int { case DISABLED = -1; diff --git a/app/Http/Controllers/api/v1/ServerController.php b/app/Http/Controllers/api/v1/ServerController.php index cca7440057..fff01117be 100644 --- a/app/Http/Controllers/api/v1/ServerController.php +++ b/app/Http/Controllers/api/v1/ServerController.php @@ -107,11 +107,12 @@ public function update(ServerRequest $request, Server $server) $server->secret = $request->secret; $server->strength = $request->strength; $server->status = $request->status; + $server->connection_status_always_online = $request->boolean('connection_status_always_online'); $server->error_count = 0; $server->recover_count = config('bigbluebutton.server_online_threshold'); - // Check if server is online/offline and update usage data + // Update server usage, load data and connection status $serverService = new ServerService($server); $serverService->updateUsage(); @@ -134,6 +135,7 @@ public function store(ServerRequest $request) $server->secret = $request->secret; $server->strength = $request->strength; $server->status = $request->status; + $server->connection_status_always_online = $request->boolean('connection_status_always_online'); $server->error_count = 0; $server->recover_count = config('bigbluebutton.server_online_threshold'); diff --git a/app/Http/Requests/ServerRequest.php b/app/Http/Requests/ServerRequest.php index d4361b65ee..dc0fe18ac0 100644 --- a/app/Http/Requests/ServerRequest.php +++ b/app/Http/Requests/ServerRequest.php @@ -19,6 +19,7 @@ public function rules() 'secret' => ['required', 'string', 'max:255'], 'strength' => ['required', 'integer', 'min:1', 'max:10'], 'status' => ['required', Rule::enum(ServerStatus::class)], + 'connection_status_always_online' => ['required', 'boolean'], ]; if ($this->route('server')) { diff --git a/app/Http/Resources/ServerResource.php b/app/Http/Resources/ServerResource.php index b511826301..4454e7fd98 100644 --- a/app/Http/Resources/ServerResource.php +++ b/app/Http/Resources/ServerResource.php @@ -41,8 +41,9 @@ public function toArray($request) 'name' => $this->name, 'description' => $this->description, 'strength' => $this->strength, + 'connection_status_always_online' => $this->connection_status_always_online, 'status' => $this->status, - 'health' => $this->health, + 'connection_status' => $this->connection_status, 'participant_count' => $this->participant_count, 'listener_count' => $this->listener_count, 'voice_participant_count' => $this->voice_participant_count, diff --git a/app/Models/Server.php b/app/Models/Server.php index 2078e67769..2a6ddb7694 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -4,7 +4,7 @@ namespace App\Models; -use App\Enums\ServerHealth; +use App\Enums\ServerConnectionStatus; use App\Enums\ServerStatus; use App\Observers\ServerObserver; use App\Traits\AddsModelNameTrait; @@ -22,6 +22,7 @@ class Server extends Model protected $casts = [ 'strength' => 'integer', + 'connection_status_always_online' => 'boolean', 'status' => ServerStatus::class, 'participant_count' => 'integer', 'listener_count' => 'integer', @@ -78,24 +79,30 @@ public function getLogLabel() return $this->name.' ('.$this->id.')'; } - public function getHealthAttribute(): ?ServerHealth + public function getConnectionStatusAttribute(): ?ServerConnectionStatus { + // No connection status available for disabled servers if ($this->status == ServerStatus::DISABLED) { return null; } - return self::calcHealth($this->recover_count, $this->error_count); + // Always return online if connection_status_always_online + if ($this->connection_status_always_online) { + return ServerConnectionStatus::ONLINE; + } + + return self::calculateConnectionStatus($this->recover_count, $this->error_count); } - public static function calcHealth(int $recover_count, int $error_count): ServerHealth + public static function calculateConnectionStatus(int $recover_count, int $error_count): ServerConnectionStatus { if ($recover_count >= config('bigbluebutton.server_online_threshold')) { - return ServerHealth::ONLINE; + return ServerConnectionStatus::ONLINE; } if ($error_count >= config('bigbluebutton.server_offline_threshold')) { - return ServerHealth::OFFLINE; + return ServerConnectionStatus::OFFLINE; } - return ServerHealth::UNHEALTHY; + return ServerConnectionStatus::FAULTY; } } diff --git a/app/Models/ServerPool.php b/app/Models/ServerPool.php index 79b790a884..2cb912d988 100644 --- a/app/Models/ServerPool.php +++ b/app/Models/ServerPool.php @@ -21,7 +21,7 @@ class ServerPool extends Model protected $fillable = ['name', 'description']; /** - * Servers that are port of this server pool + * Servers that are part of this server pool */ public function servers(): BelongsToMany { diff --git a/app/Observers/ServerObserver.php b/app/Observers/ServerObserver.php index 4ef07782ad..6fa1ddbcea 100644 --- a/app/Observers/ServerObserver.php +++ b/app/Observers/ServerObserver.php @@ -4,7 +4,7 @@ namespace App\Observers; -use App\Enums\ServerHealth; +use App\Enums\ServerConnectionStatus; use App\Enums\ServerStatus; use App\Models\Server; use Illuminate\Support\Facades\Log; @@ -54,28 +54,28 @@ public function updated(Server $server): void ]); } - // Check if server health changed - $newHealth = Server::calcHealth($server->recover_count, $server->error_count); - $previousHealth = Server::calcHealth($server->getOriginal('recover_count'), $server->getOriginal('error_count')); - if ($newHealth != $previousHealth) { - if ($newHealth == ServerHealth::OFFLINE) { - Log::error('Server {server} health changed to offline', [ + // Check if server connection status changed + $newConnectionStatus = Server::calculateConnectionStatus($server->recover_count, $server->error_count); + $previousConnectionStatus = Server::calculateConnectionStatus($server->getOriginal('recover_count'), $server->getOriginal('error_count')); + if ($newConnectionStatus != $previousConnectionStatus) { + if ($newConnectionStatus == ServerConnectionStatus::OFFLINE) { + Log::error('Server {server} changed to offline', [ 'server' => $server->getLogLabel(), - 'old_health' => $previousHealth->name, + 'old_connection_status' => $previousConnectionStatus->name, ]); } - if ($newHealth == ServerHealth::UNHEALTHY) { - Log::warning('Server {server} health changed to unhealthy', [ + if ($newConnectionStatus == ServerConnectionStatus::FAULTY) { + Log::warning('Server {server} changed to faulty', [ 'server' => $server->getLogLabel(), - 'old_health' => $previousHealth->name, + 'old_connection_status' => $previousConnectionStatus->name, ]); } - if ($newHealth == ServerHealth::ONLINE) { - Log::notice('Server {server} health changed to healthy', [ + if ($newConnectionStatus == ServerConnectionStatus::ONLINE) { + Log::notice('Server {server} changed to online', [ 'server' => $server->getLogLabel(), - 'old_health' => $previousHealth->name, + 'old_connection_status' => $previousConnectionStatus->name, ]); } } diff --git a/app/Prometheus/Collectors/ServerCollector.php b/app/Prometheus/Collectors/ServerCollector.php index c0469ad50e..de615dd3a9 100644 --- a/app/Prometheus/Collectors/ServerCollector.php +++ b/app/Prometheus/Collectors/ServerCollector.php @@ -30,6 +30,6 @@ public function collect(): void ->set(Server::where('status', ServerStatus::ENABLED) ->where('recover_count', '<', config('bigbluebutton.server_online_threshold')) ->where('error_count', '<', config('bigbluebutton.server_offline_threshold')) - ->count(), ['unhealthy']); + ->count(), ['faulty']); } } diff --git a/app/Services/LoadBalancingService.php b/app/Services/LoadBalancingService.php index 62b4025d70..4a6e98369d 100644 --- a/app/Services/LoadBalancingService.php +++ b/app/Services/LoadBalancingService.php @@ -7,14 +7,15 @@ use App\Enums\ServerStatus; use App\Models\Server; use App\Models\ServerPool; +use Illuminate\Database\Eloquent\Builder; class LoadBalancingService { - private $servers; + private ServerPool $serverPool; public function setServerPool(ServerPool $serverPool) { - $this->servers = $serverPool->servers; + $this->serverPool = $serverPool; return $this; } @@ -24,14 +25,16 @@ public function setServerPool(ServerPool $serverPool) */ public function getLowestUsageServer(): ?Server { - return $this->servers + return $this->serverPool->servers() ->where('status', ServerStatus::ENABLED) - ->where('recover_count', '>=', config('bigbluebutton.server_online_threshold')) - ->where('error_count', '=', 0) - ->whereNotNull('load') - ->sortBy(function (Server $server) { - return $server->load / $server->strength; + ->where(function (Builder $query) { + $query->where('recover_count', '>=', config('bigbluebutton.server_online_threshold')) + ->where('error_count', '=', 0) + ->orWhere('connection_status_always_online', true); }) + ->whereNotNull('load') + ->where('strength', '>', 0) // Extra safety against division by zero; request validation ensures strength is between 1 and 10 + ->orderByRaw('`load` / `strength`') ->first(); } } diff --git a/app/Services/ServerService.php b/app/Services/ServerService.php index 281bdd6bda..d69e5c1684 100644 --- a/app/Services/ServerService.php +++ b/app/Services/ServerService.php @@ -4,7 +4,7 @@ namespace App\Services; -use App\Enums\ServerHealth; +use App\Enums\ServerConnectionStatus; use App\Enums\ServerStatus; use App\Models\Meeting; use App\Models\MeetingStat; @@ -87,7 +87,11 @@ private function getBBBVersion(): ?string */ public function handleApiCallFailed() { - if ($this->server->health != ServerHealth::OFFLINE) { + if ($this->server->connection_status_always_online) { + return; + } + + if ($this->server->connection_status != ServerConnectionStatus::OFFLINE) { $this->server->error_count++; } @@ -96,23 +100,25 @@ public function handleApiCallFailed() $this->server->timestamps = false; $this->server->save(); - if ($this->server->health == ServerHealth::OFFLINE) { + if ($this->server->connection_status == ServerConnectionStatus::OFFLINE) { $this->setMeetingsDetached(); } } public function handleApiCallSuccessful() { - if ($this->server->health != ServerHealth::ONLINE) { - $this->server->recover_count++; - } + if (! $this->server->connection_status_always_online) { + if ($this->server->connection_status != ServerConnectionStatus::ONLINE) { + $this->server->recover_count++; + } - if ($this->server->health == ServerHealth::ONLINE) { - $this->server->error_count = 0; - } + if ($this->server->connection_status == ServerConnectionStatus::ONLINE) { + $this->server->error_count = 0; + } - $this->server->timestamps = false; - $this->server->save(); + $this->server->timestamps = false; + $this->server->save(); + } $this->endDetachedMeetings(); @@ -206,7 +212,7 @@ public function updateUsage($updateServerStatistics = false, $updateMeetingStati $this->server->stats()->save($serverStat); } - if ($this->server->health == ServerHealth::OFFLINE) { + if ($this->server->connection_status == ServerConnectionStatus::OFFLINE) { // Clear current live server status $this->server->participant_count = null; $this->server->listener_count = null; diff --git a/database/factories/ServerFactory.php b/database/factories/ServerFactory.php index a250715ae2..fbfb3b751e 100644 --- a/database/factories/ServerFactory.php +++ b/database/factories/ServerFactory.php @@ -34,6 +34,7 @@ public function definition() 'version' => '2.4.5', 'strength' => 1, 'load' => 0, + 'connection_status_always_online' => false, ]; } } diff --git a/database/migrations/2026_07_17_000000_add_connection_status_always_online_to_servers_table.php b/database/migrations/2026_07_17_000000_add_connection_status_always_online_to_servers_table.php new file mode 100644 index 0000000000..e0360a63ad --- /dev/null +++ b/database/migrations/2026_07_17_000000_add_connection_status_always_online_to_servers_table.php @@ -0,0 +1,30 @@ +boolean('connection_status_always_online')->default(false)->after('strength'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('connection_status_always_online'); + }); + } +}; diff --git a/database/migrations/migrate-to-v4/2024_03_06_152142_add_health_counters_to_servers_table.php b/database/migrations/migrate-to-v4/2024_03_06_152142_add_health_counters_to_servers_table.php index 4a6eb2c198..e8ecb4a22a 100644 --- a/database/migrations/migrate-to-v4/2024_03_06_152142_add_health_counters_to_servers_table.php +++ b/database/migrations/migrate-to-v4/2024_03_06_152142_add_health_counters_to_servers_table.php @@ -20,24 +20,21 @@ public function up(): void $table->integer('recover_count')->default(0); }); - // Migrate the status column to health counters and status + // Migrate the status column to connection status counters and status foreach (Server::all() as $server) { switch ($server->getRawOriginal('status')) { - // Disabled - case -1: + case -1: // Disabled $server->status = ServerStatus::DISABLED; break; - // Offline - case 0: - // Server is unhealthy, but not offline yet + case 0: // Offline + // Server is faulty, but not offline yet $server->recover_count = 0; $server->error_count = 0; $server->status = ServerStatus::ENABLED; break; - // Online - case 1: - // Server is healthy + case 1: // Online + // Server is online $server->recover_count = config('bigbluebutton.server_online_threshold'); $server->error_count = 0; $server->status = ServerStatus::ENABLED; diff --git a/docs/static/grafana-dashboard.json b/docs/static/grafana-dashboard.json index b463e12e81..56761c5d8f 100644 --- a/docs/static/grafana-dashboard.json +++ b/docs/static/grafana-dashboard.json @@ -604,7 +604,7 @@ { "matcher": { "id": "byName", - "options": "pilos_servers_total{instance=\"app:80\", job=\"prometheus\", status=\"unhealthy\"}" + "options": "pilos_servers_total{instance=\"app:80\", job=\"prometheus\", status=\"faulty\"}" }, "properties": [ { @@ -713,12 +713,12 @@ }, "disableTextWrap": false, "editorMode": "builder", - "expr": "pilos_servers_total{status=\"unhealthy\"}", + "expr": "pilos_servers_total{status=\"faulty\"}", "fullMetaSearch": false, "hide": false, "includeNullMetadata": true, "range": true, - "refId": "Unhealthy", + "refId": "Faulty", "useBackend": false } ], diff --git a/lang/en/admin.php b/lang/en/admin.php index 050301112b..0ce867b3e7 100644 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -213,6 +213,7 @@ 'view' => 'Detailed information for the server pool :name', ], 'servers' => [ + 'always_online' => 'Always online', 'base_url' => 'API endpoint', 'connection' => 'Connection', 'current_usage' => 'Current usage', @@ -226,6 +227,7 @@ 'draining' => 'Draining', 'edit' => 'Edit server :name', 'enabled' => 'Enabled', + 'faulty' => 'Faulty', 'flash' => [ 'panic' => [ 'description_meetings_successful' => '{0} No meetings were successfully stopped. |{1} 1 meeting was successfully stopped. |[2,*] :count meetings were successfully stopped.', @@ -259,9 +261,10 @@ 'status' => 'Status', 'strength' => 'Server strength', 'strength_description' => 'Load balancing factor; the higher the factor, the more participants and meetings the server can handle', + 'connection_status_always_online' => 'Connection always online', + 'connection_status_always_online_description' => 'The server is always considered online and available for new meetings; connection issues are ignored', 'test_connection' => 'Test connection', 'tile_description' => 'The servers provide the BBB infrastructure for the meetings.', - 'unhealthy' => 'Faulty', 'unknown' => 'Unknown', 'usage_info' => 'The usage (meetings, participants, videos) also contains meetings that are managed by other systems.', 'version' => 'Version', diff --git a/resources/js/views/AdminServersIndex.vue b/resources/js/views/AdminServersIndex.vue index 37c1efd9b0..dd52d008e7 100644 --- a/resources/js/views/AdminServersIndex.vue +++ b/resources/js/views/AdminServersIndex.vue @@ -146,10 +146,13 @@ /> - +