Zabbix integration

This commit is contained in:
JonatanRek 2024-08-16 23:37:42 +02:00
parent f3a3dcb566
commit b749cdf591
13 changed files with 214 additions and 67 deletions

View File

@ -15,22 +15,15 @@ class HostController extends BaseController
public function sync() public function sync()
{ {
$zabbix = new ZabbixService("https://zabbix.itego.cz"); $zabbix = new ZabbixService(config('zabbix.url'));
$zabbix->connect("spaninger", "*"); $zabbix->connect(config('zabbix.username'), config('zabbix.password'));
foreach ( $zabbix->maintenances([ "selectTimeperiods" => "extend" ]) as $maintennace ) {
if(Carbon::createFromTimestamp($maintennace['active_till'])->isPast()){
continue;
}
dd(Carbon::createFromTimestamp($maintennace['active_till']));
}
foreach ($zabbix->hosts() as $host) { foreach ($zabbix->hosts() as $host) {
Host::updateOrCreate([ Host::updateOrCreate([
"hostname" => $host['host'], "hostname" => $host['host'],
], [ ], [
"display_name" => $host['name'], "display_name" => $host['name'],
"external_id" => $host['hostid'],
]); ]);
} }

View File

@ -21,6 +21,8 @@ class MaintenanceController extends BaseController
public function plannedDetail(MaintenanceHistory $maintenance_history) public function plannedDetail(MaintenanceHistory $maintenance_history)
{ {
if (!empty($maintenance_history->finished_at)) { if (!empty($maintenance_history->finished_at)) {
abort(404); abort(404);
} }
@ -96,4 +98,73 @@ class MaintenanceController extends BaseController
'maintenance_history' => $maintenance_history ?? [] '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();
}
} }

View File

@ -36,7 +36,7 @@ class ScheduleNextMaintenance implements ShouldQueue
$cron = new CronCronExpression($maintenance->schedule); $cron = new CronCronExpression($maintenance->schedule);
$nextRunTime = Carbon::createFromTimestamp($cron->getNext()); $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; continue;
}; };

View File

@ -12,6 +12,7 @@ class Host extends Model
protected $fillable = [ protected $fillable = [
'hostname', 'hostname',
'display_name', 'display_name',
'external_id',
]; ];
public function tasks() public function tasks()

View File

@ -17,6 +17,7 @@ class Maintenance extends Model
'blocking_maintenance_id', 'blocking_maintenance_id',
'duration', 'duration',
'blocking_maintenance_offset', 'blocking_maintenance_offset',
'external_id',
]; ];
public function hosts() public function hosts()

View File

@ -17,6 +17,10 @@ class MaintenanceHistory extends Model
'guestor_id', 'guestor_id',
]; ];
protected $casts = [
'start_at' => 'datetime',
'finished_at' => 'datetime',
];
protected static function booted() protected static function booted()
{ {

View File

@ -2,6 +2,7 @@
namespace App\Services; namespace App\Services;
use Carbon\Carbon;
use Exception; use Exception;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
@ -11,92 +12,51 @@ class ZabbixService
public string $url = ""; public string $url = "";
public int $id = 1; public int $id = 1;
function __construct($url) { function __construct($url)
{
$this->url = $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", "jsonrpc" => "2.0",
"method" => "user.login", "method" => "user.login",
"params" => [ "params" => [
"username" => $username, "username" => $username,
"password" => $password, "password" => $password,
], ],
"id" => $this->id, "id" => $this->id,
"auth" => null, "auth" => null,
]); ]);
return $this->token;
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++;
} }
public function hosts($params = []) public function hosts($params = [])
{ {
if (empty($this->token)) { return $this->request([
throw new Exception("you need to connect first", 1);
}
$response = Http::withoutVerifying()->post($this->url . "/api_jsonrpc.php", [
"jsonrpc" => "2.0", "jsonrpc" => "2.0",
"method" => "host.get", "method" => "host.get",
"params" => $params, "params" => $params,
"id" => $this->id, "id" => $this->id,
"auth" => $this->token, "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 = []) public function hostGroups($params = [])
{ {
if (empty($this->token)) { return $this->request([
throw new Exception("you need to connect first", 1);
}
$response = Http::withoutVerifying()->post($this->url . "/api_jsonrpc.php", [
"jsonrpc" => "2.0", "jsonrpc" => "2.0",
"method" => "hostgroup.get", "method" => "hostgroup.get",
"params" => $params, "params" => $params,
"id" => $this->id, "id" => $this->id,
"auth" => $this->token, "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 = []){ public function maintenances($params = [])
return $this->request( [ {
return $this->request([
"jsonrpc" => "2.0", "jsonrpc" => "2.0",
"method" => "maintenance.get", "method" => "maintenance.get",
"params" => $params, "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*/ /*Helpers*/
private function request($body = []){ private function request($body = [])
if (empty($this->token)) { {
if (empty($this->token) && $body['auth'] != null) {
throw new Exception("you need to connect first", 1); throw new Exception("you need to connect first", 1);
} }
@ -125,6 +107,6 @@ class ZabbixService
} }
$this->id++; $this->id++;
return collect($responseObject["result"]); return is_array($responseObject["result"]) ? collect($responseObject["result"]) : $responseObject["result"];
} }
} }

7
config/zabbix.php Normal file
View File

@ -0,0 +1,7 @@
<?php
return [
'url' => env('ZBX_URL', ''),
'username' => env('ZBX_USERNAME', ''),
'password' => env('ZBX_PASSWORD', ''),
];

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('hosts', function (Blueprint $table) {
$table->string('external_id')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('hosts', function (Blueprint $table) {
});
}
};

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('maintenances', function (Blueprint $table) {
$table->string('external_id')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('maintenances', function (Blueprint $table) {
//
});
}
};

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('maintenance_histories', function (Blueprint $table) {
$table->string('external_id')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('maintenance_histories', function (Blueprint $table) {
//
});
}
};

View File

@ -2,6 +2,9 @@
<div class="container-xl"> <div class="container-xl">
<div class="page-header"> <div class="page-header">
<h1>{{ __('Planned Maintenance') }}</h1> <h1>{{ __('Planned Maintenance') }}</h1>
<a class="btn btn-primary" href="{{ route('maintenance.start', ['maintenance_history' => $maintenance_history->id]) }}" disabled>
<i class="me-2 fas fa-play"></i><span>{{ __('Zahájit') }}</span>
</a>
</div> </div>
<p>{!! $maintenance_history->maintenance->description !!}</p> <p>{!! $maintenance_history->maintenance->description !!}</p>
<x-form::form action="{{ route('maintenance.planned.detail.put', ['maintenance_history' => $maintenance_history->id]) }}" method="PUT"> <x-form::form action="{{ route('maintenance.planned.detail.put', ['maintenance_history' => $maintenance_history->id]) }}" method="PUT">

View File

@ -10,6 +10,7 @@ Route::get('/', function () {
Route::get('/maintenance/planned', [App\Http\Controllers\MaintenanceController::class, 'planned'])->name('maintenance.planned'); 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::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::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::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/', [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'); Route::get('/maintenance/history/{maintenance_history}', [App\Http\Controllers\MaintenanceController::class, 'historyDetail'])->name('maintenance.history.detail');