From b749cdf591c1ee13071874c0ed7f6c8ccd6a7683 Mon Sep 17 00:00:00 2001 From: JonatanRek Date: Fri, 16 Aug 2024 23:37:42 +0200 Subject: [PATCH] Zabbix integration --- app/Http/Controllers/HostController.php | 13 +-- .../Controllers/MaintenanceController.php | 71 ++++++++++++++ app/Jobs/ScheduleNextMaintenance.php | 2 +- app/Models/Host.php | 1 + app/Models/Maintenance.php | 1 + app/Models/MaintenanceHistory.php | 4 + app/Services/ZabbixService.php | 94 ++++++++----------- config/zabbix.php | 7 ++ ...6_202249_add_external_id_to_host_table.php | 28 ++++++ ...7_add_external_id_to_maintenance_table.php | 28 ++++++ ...ternal_id_to_maintenance_history_table.php | 28 ++++++ .../maintenance/planned-detail.blade.php | 3 + routes/web.php | 1 + 13 files changed, 214 insertions(+), 67 deletions(-) create mode 100644 config/zabbix.php create mode 100644 database/migrations/2024_08_16_202249_add_external_id_to_host_table.php create mode 100644 database/migrations/2024_08_16_203457_add_external_id_to_maintenance_table.php create mode 100644 database/migrations/2024_08_16_211238_add_external_id_to_maintenance_history_table.php diff --git a/app/Http/Controllers/HostController.php b/app/Http/Controllers/HostController.php index 4858386..a97698e 100644 --- a/app/Http/Controllers/HostController.php +++ b/app/Http/Controllers/HostController.php @@ -15,22 +15,15 @@ class HostController extends BaseController public function sync() { - $zabbix = new ZabbixService("https://zabbix.itego.cz"); - $zabbix->connect("spaninger", "*"); - - foreach ( $zabbix->maintenances([ "selectTimeperiods" => "extend" ]) as $maintennace ) { - if(Carbon::createFromTimestamp($maintennace['active_till'])->isPast()){ - continue; - } - - dd(Carbon::createFromTimestamp($maintennace['active_till'])); - } + $zabbix = new ZabbixService(config('zabbix.url')); + $zabbix->connect(config('zabbix.username'), config('zabbix.password')); foreach ($zabbix->hosts() as $host) { Host::updateOrCreate([ "hostname" => $host['host'], ], [ "display_name" => $host['name'], + "external_id" => $host['hostid'], ]); } diff --git a/app/Http/Controllers/MaintenanceController.php b/app/Http/Controllers/MaintenanceController.php index 304b9a8..f5b48c8 100644 --- a/app/Http/Controllers/MaintenanceController.php +++ b/app/Http/Controllers/MaintenanceController.php @@ -21,6 +21,8 @@ class MaintenanceController extends BaseController public function plannedDetail(MaintenanceHistory $maintenance_history) { + + if (!empty($maintenance_history->finished_at)) { abort(404); } @@ -96,4 +98,73 @@ class MaintenanceController extends BaseController 'maintenance_history' => $maintenance_history ?? [] ]); } + + public function start(Request $request, MaintenanceHistory $maintenance_history) + { + + $hostsForMaintenance = []; + + foreach ($maintenance_history->historyHosts as $historyHost) { + if (empty($historyHost->host->external_id)) { + continue; + } + + $hostsForMaintenance[] = [ + "hostid" => (int) $historyHost->host->external_id, + ]; + } + + $zabbix = new ZabbixService(config('zabbix.url')); + $zabbix->connect(config('zabbix.username'), config('zabbix.password')); + if (empty($maintenance_history->maintenance->external_id)) { + $maintenance_history->maintenance->external_id = $zabbix->maintenancesCreate([ + "name" => $maintenance_history->maintenance->name . ' - ' . $maintenance_history->start_at->format('Y'), + "description" => strip_tags($maintenance_history->maintenance->description), + "active_since" => $maintenance_history->start_at->startofyear()->timestamp, + "active_till" => $maintenance_history->start_at->endofyear()->timestamp, + "tags_evaltype" => 0, + "hosts" => $hostsForMaintenance, + "timeperiods" => [[ + "timeperiod_type" => 0, + "start_date" => $maintenance_history->start_at->timestamp, + "period" => (($maintenance_history->maintenance->duration * 60) * 60), + ]], + ])['maintenanceids'][0]; + $maintenance_history->maintenance->save(); + } else { + $createPeriode = true; + $timePeriodes = $zabbix->maintenances([ + "selectTimeperiods" => "extend", + 'maintenanceids' => $maintenance_history->maintenance->external_id, + ])[0]['timeperiods']; + + foreach ($timePeriodes as $periode) { + if ((int) $periode['start_date'] === $maintenance_history->start_at->timestamp) { + $createPeriode = false; + } + } + + if ($createPeriode) { + for ($i=0; $i < count($timePeriodes); $i++) { + unset($timePeriodes[$i]['every']); + unset($timePeriodes[$i]['month']); + unset($timePeriodes[$i]['dayofweek']); + unset($timePeriodes[$i]['day']); + unset($timePeriodes[$i]['start_time']); + } + $timePeriodes[] = [ + "timeperiod_type" => 0, + "start_date" => $maintenance_history->start_at->timestamp, + "period" => (($maintenance_history->maintenance->duration * 60) * 60), + ]; + + $zabbix->maintenancesUpdate([ + "maintenanceid" => $maintenance_history->maintenance->external_id, + "timeperiods" => $timePeriodes, + ]); + } + } + + return redirect()->back(); + } } diff --git a/app/Jobs/ScheduleNextMaintenance.php b/app/Jobs/ScheduleNextMaintenance.php index 7f9b220..7f13d34 100644 --- a/app/Jobs/ScheduleNextMaintenance.php +++ b/app/Jobs/ScheduleNextMaintenance.php @@ -36,7 +36,7 @@ class ScheduleNextMaintenance implements ShouldQueue $cron = new CronCronExpression($maintenance->schedule); $nextRunTime = Carbon::createFromTimestamp($cron->getNext()); - if(MaintenanceHistory::where('hash', md5($maintenance->id . $nextRunTime))->first() === null){ + if(MaintenanceHistory::where('hash', md5($maintenance->id . $nextRunTime))->first() !== null){ continue; }; diff --git a/app/Models/Host.php b/app/Models/Host.php index ec3ea44..78b711a 100644 --- a/app/Models/Host.php +++ b/app/Models/Host.php @@ -12,6 +12,7 @@ class Host extends Model protected $fillable = [ 'hostname', 'display_name', + 'external_id', ]; public function tasks() diff --git a/app/Models/Maintenance.php b/app/Models/Maintenance.php index 24d07dc..3f3b9c0 100644 --- a/app/Models/Maintenance.php +++ b/app/Models/Maintenance.php @@ -17,6 +17,7 @@ class Maintenance extends Model 'blocking_maintenance_id', 'duration', 'blocking_maintenance_offset', + 'external_id', ]; public function hosts() diff --git a/app/Models/MaintenanceHistory.php b/app/Models/MaintenanceHistory.php index a756349..630f222 100644 --- a/app/Models/MaintenanceHistory.php +++ b/app/Models/MaintenanceHistory.php @@ -17,6 +17,10 @@ class MaintenanceHistory extends Model 'guestor_id', ]; + protected $casts = [ + 'start_at' => 'datetime', + 'finished_at' => 'datetime', + ]; protected static function booted() { diff --git a/app/Services/ZabbixService.php b/app/Services/ZabbixService.php index 10ca353..b7fa49e 100644 --- a/app/Services/ZabbixService.php +++ b/app/Services/ZabbixService.php @@ -2,6 +2,7 @@ namespace App\Services; +use Carbon\Carbon; use Exception; use Illuminate\Support\Facades\Http; @@ -11,92 +12,51 @@ class ZabbixService public string $url = ""; public int $id = 1; - function __construct($url) { + function __construct($url) + { $this->url = $url; } - public function connect( $username, $password) + public function connect($username, $password) { - $response = Http::withoutVerifying()->post($this->url . "/api_jsonrpc.php", [ + $this->token = $this->request([ "jsonrpc" => "2.0", "method" => "user.login", "params" => [ - "username" => $username, - "password" => $password, + "username" => $username, + "password" => $password, ], "id" => $this->id, "auth" => null, ]); - - if (!$response->successful()) { - throw new Exception("Unable To Authenticated", 1); - } - - $responseObject = $response->json(); - if (isset($responseObject['error'])) { - throw new Exception($responseObject['error']['data'], $responseObject['error']['code']); - } - - $this->token = $response['result']; - $this->id++; + return $this->token; } public function hosts($params = []) { - if (empty($this->token)) { - throw new Exception("you need to connect first", 1); - } - - $response = Http::withoutVerifying()->post($this->url . "/api_jsonrpc.php", [ + return $this->request([ "jsonrpc" => "2.0", "method" => "host.get", "params" => $params, "id" => $this->id, "auth" => $this->token, ]); - - if (!$response->successful()) { - throw new Exception("Unable To Request", 1); - } - - $responseObject = $response->json(); - if (isset($responseObject['error'])) { - throw new Exception($responseObject['error']['data'], $responseObject['error']['code']); - } - - $this->id++; - return collect($responseObject["result"]); } public function hostGroups($params = []) { - if (empty($this->token)) { - throw new Exception("you need to connect first", 1); - } - - $response = Http::withoutVerifying()->post($this->url . "/api_jsonrpc.php", [ + return $this->request([ "jsonrpc" => "2.0", "method" => "hostgroup.get", "params" => $params, "id" => $this->id, "auth" => $this->token, ]); - - if (!$response->successful()) { - throw new Exception("Unable To Request", 1); - } - - $responseObject = $response->json(); - if (isset($responseObject['error'])) { - throw new Exception($responseObject['error']['data'], $responseObject['error']['code']); - } - - $this->id++; - return collect($responseObject["result"]); } - public function maintenances($params = []){ - return $this->request( [ + public function maintenances($params = []) + { + return $this->request([ "jsonrpc" => "2.0", "method" => "maintenance.get", "params" => $params, @@ -105,11 +65,33 @@ class ZabbixService ]); } + public function maintenancesCreate($params = []) + { + return $this->request([ + "jsonrpc" => "2.0", + "method" => "maintenance.create", + "params" => $params, + "id" => $this->id, + "auth" => $this->token, + ]); + } + + public function maintenancesUpdate( $params = []) + { + return $this->request([ + "jsonrpc" => "2.0", + "method" => "maintenance.update", + "params" => $params, + "id" => $this->id, + "auth" => $this->token, + ]); + } /*Helpers*/ - private function request($body = []){ - if (empty($this->token)) { + private function request($body = []) + { + if (empty($this->token) && $body['auth'] != null) { throw new Exception("you need to connect first", 1); } @@ -125,6 +107,6 @@ class ZabbixService } $this->id++; - return collect($responseObject["result"]); + return is_array($responseObject["result"]) ? collect($responseObject["result"]) : $responseObject["result"]; } } diff --git a/config/zabbix.php b/config/zabbix.php new file mode 100644 index 0000000..00e83cd --- /dev/null +++ b/config/zabbix.php @@ -0,0 +1,7 @@ + env('ZBX_URL', ''), + 'username' => env('ZBX_USERNAME', ''), + 'password' => env('ZBX_PASSWORD', ''), +]; diff --git a/database/migrations/2024_08_16_202249_add_external_id_to_host_table.php b/database/migrations/2024_08_16_202249_add_external_id_to_host_table.php new file mode 100644 index 0000000..f60fb16 --- /dev/null +++ b/database/migrations/2024_08_16_202249_add_external_id_to_host_table.php @@ -0,0 +1,28 @@ +string('external_id')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('hosts', function (Blueprint $table) { + + }); + } +}; diff --git a/database/migrations/2024_08_16_203457_add_external_id_to_maintenance_table.php b/database/migrations/2024_08_16_203457_add_external_id_to_maintenance_table.php new file mode 100644 index 0000000..e316cb0 --- /dev/null +++ b/database/migrations/2024_08_16_203457_add_external_id_to_maintenance_table.php @@ -0,0 +1,28 @@ +string('external_id')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('maintenances', function (Blueprint $table) { + // + }); + } +}; diff --git a/database/migrations/2024_08_16_211238_add_external_id_to_maintenance_history_table.php b/database/migrations/2024_08_16_211238_add_external_id_to_maintenance_history_table.php new file mode 100644 index 0000000..75205e4 --- /dev/null +++ b/database/migrations/2024_08_16_211238_add_external_id_to_maintenance_history_table.php @@ -0,0 +1,28 @@ +string('external_id')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('maintenance_histories', function (Blueprint $table) { + // + }); + } +}; diff --git a/resources/views/maintenance/planned-detail.blade.php b/resources/views/maintenance/planned-detail.blade.php index abc203d..55e3bc9 100644 --- a/resources/views/maintenance/planned-detail.blade.php +++ b/resources/views/maintenance/planned-detail.blade.php @@ -2,6 +2,9 @@

{!! $maintenance_history->maintenance->description !!}

diff --git a/routes/web.php b/routes/web.php index b1b376c..40d1f55 100644 --- a/routes/web.php +++ b/routes/web.php @@ -10,6 +10,7 @@ Route::get('/', function () { Route::get('/maintenance/planned', [App\Http\Controllers\MaintenanceController::class, 'planned'])->name('maintenance.planned'); Route::get('/maintenance/planned/{maintenance_history}', [App\Http\Controllers\MaintenanceController::class, 'plannedDetail'])->name('maintenance.planned.detail'); Route::put('/maintenance/planned/{maintenance_history}', [App\Http\Controllers\MaintenanceController::class, 'plannedDetailPut'])->name('maintenance.planned.detail.put'); +Route::get('/maintenance/planned/{maintenance_history}/start', [App\Http\Controllers\MaintenanceController::class, 'start'])->name('maintenance.start'); Route::post('/maintenance/planned/{maintenance_history}/finished', [App\Http\Controllers\MaintenanceController::class, 'plannedDetailFinishPost'])->name('maintenance.planned.finished'); Route::get('/maintenance/history/', [App\Http\Controllers\MaintenanceController::class, 'history'])->name('maintenance.history'); Route::get('/maintenance/history/{maintenance_history}', [App\Http\Controllers\MaintenanceController::class, 'historyDetail'])->name('maintenance.history.detail');