Мониторинг серверов Trassir | Путешествуем по всему миру!

Avito Путешествия

Мониторинг серверов Trassir

Так сложилось, что есть необходимость администрирования большого количества (более 50 и будет кратно больше) серверов Trassir (сервера видеонаблюдения) расположенных в разных городах СНГ. В целом оборудование не плохое, но присутствуют проблемы с централизованным управлением в силу особенностей архитектуры системы, каждый сервер (NVR) живет своей жизнью. Некоторые возможности порулить сразу кучей устройств дает «родное» облако, но оно абсолютно неконфигурируемое и использовать его можно лишь как оно есть. Утро каждого рабочего дня начинается с проверки каждого сервера на работоспособность, что отнимает очень много времени. У DSSL есть SDK, полное описание доступно тут. Потратил пару вечеров и написал класс на php, который позволяет веб-серверу проверять состояние Trassir серверов и выводить на страничку. Для доступа к Trassir серверам через web их необходимо сконфигурировать. Во-первых в настройках сервера включить «Разрешить доступ к Trassir из браузера», во — вторых «Trassir SDK» (и задать SDK пароль). Кроме того, рекомендую сервер trassir купить и создать пользователя с усеченными правами для авторизации скрипта (в моем случае пользователь Monitoring, пароль 123, SDK пароль 12345). image В целом SDK дает огромные возможности, ознакомиться можно по ссылке что я давал выше. Сам код класса:
<?php
class TrassirServer {
/*
	1. Создаем новый объект. $serv = new TrassirServer('10.18.242.33', 'Monitoring', '123', '12345');
	2. Обязательно проверяем онлайн ли он прежде чем работать с ним дальше! $serv->check_connection();
	3. Получение сессии $serv->get_sid();
	4. Получение списка объектов как массив $objects = $serv->get_objects(); Необходим пароль SDK
	5. Получение здоровья $serv->get_health(); Обязательно перед этим выполнить пункт 3 (get_sid())
*/
	public $status = array();
	//public $objects = array();
	private $ip_address, $user, $sid, $sdk_sid;
	public function __construct($ip_address, $user, $password, $sdk_password) { //конструктор.
		$this->ip_address = $ip_address;
		$this->status['ip_address']= NULL;
		$this->user = $user;
		$this->password = $password;
		$this->sdk_password = $sdk_password;
   }		
	public function check_connection (){ //проверка доступности сервера.
		$url = 'http://'.trim($this->ip_address).':80/';
		$curlInit = curl_init($url);
		curl_setopt($curlInit,CURLOPT_CONNECTTIMEOUT,2); //третий параметр - время ожидания ответа сервера в секундах
		curl_setopt($curlInit,CURLOPT_HEADER,true);
		curl_setopt($curlInit,CURLOPT_NOBODY,true);
		curl_setopt($curlInit,CURLOPT_RETURNTRANSFER,true);
		$response = curl_exec($curlInit);
		curl_close($curlInit);
		if ($response){
			$this->status['online'] = true;
		} else {
			$this->status['online'] = false;
		}
		return $this->status['online'];
	}	
	/*    {
        "success" : "0",
        "error_code" : "invalid username or password"
    }

	/*

	Username and Password should match to one of the server users.

		*/
	public function get_sid(){  //получение сессии
		if ($this->status['online']){
			$url = 'https://' . trim($this->ip_address) . ':8080/login?username=' . $this->user  . '&password='.$this->password; //получаем объекты сервера по адресу		
			$responseJson_str = file_get_contents ($url);			
			$server_auth = json_decode ($responseJson_str, true); //переводим JSON в массив
			if($server_auth['success']==1){
				$this->status['sid'] = $server_auth['sid']; //записываем sid массив
			}	
			else{
				$this->status['sid'] = false;
			}
		}	
		return $this->status['sid'];	
	}		
	public function get_objects(){//получаем объекты сервера
		if ($this->status['online']){		
			$url = 'https://' . trim($this->ip_address) . ':8080/objects/?password='.$this->sdk_password;
			$responseJson_str = file_get_contents ($url); //получаем объекты сервера по адресу		
			$comment_position = strripos ($responseJson_str, '/*');	//отрезаем комментарий в конце ответа сервера
			$responseJson_str = substr ($responseJson_str, 0, $comment_position);
				$objects = json_decode ($responseJson_str, true); 			
			return $objects;
		}
		return false;
	}	
	/* пример вовзращаемого массива 
	{
    "disks": "1",
    "database": "1",
    "channels_total": "10",
    "channels_online": "5",
    "uptime": "12902",
    "cpu_load": "22.50",
    "network": "1",
    "automation": "1",
    "disks_stat_main_days": "56.15",
    "disks_stat_priv_days": "35.03",
    "disks_stat_subs_days": "40.20"
	}
	*/
	public function get_health() {	
		if ($this->status['online'] && $this->status['sid']){		
			$url = 'https://' . trim($this->ip_address) . ':8080/health?sid='.$this->status['sid'];
			$responseJson_str = file_get_contents ($url); //получаем состояние сервера по адресу		
			$comment_position = strripos ($responseJson_str, '/*');	//отрезаем комментарий в конце ответа сервера
			$responseJson_str = substr ($responseJson_str, 0, $comment_position);
			$server_health = json_decode ($responseJson_str, true); //переводим JSON в массив
		}
	return $server_health;
	}
}
?>
Пример использования подразумевает наличие трех файлов, index.php, view.css (таблица стилей, необязательна, но без нее все будет грустно), list_of_servers.txt (текстовый файл, в котором указаны IP адреса всех серверов для проверки, каждый с новой строки). view.css:
.error{
	background-color: cc3f5b;
	#border: 1px dotted red;
	#width: 99%;
	padding-left: 5px;
	
}

.trassir_server{
	#border-bottom: 1px solid black;
	width: 250px;
	height: 240px;
	background-color: #4682B4;
	color: white;
	margin-top: 15px;
	margin-left: 5px;
	display: inline-block;
	vertical-align: top;

}

.OK{
	background-color:  #4169E0;	
	border-bottom: 1px solid black;
	padding-left: 5px;
}

.trassir_server_name{
		font-size: 20px;
		text-align: center;
		height: 30px;
		
}

body{
		background-color: #DCDCDC;
}
index.php
<?php
	header('Content-Type: text/html; charset=utf-8');
	ini_set('max_execution_time', 60);	
	error_reporting(E_ALL);	
	require ('classes/TrassirServer.php');
?>
	<html>
	<head>
		<link rel='stylesheet' href='./css/view.css'>
	</head>
	<body>
<?php
	$user = 'Monitoring';
	$password = '123';
	$sdk_password = '12345';

function trassir_server_monitor($ip, $user, $password, $sdk_password){	
	$serv = new TrassirServer($ip, $user, $password, $sdk_password);
	echo '<div class = "trassir_server">';
		if ($serv->check_connection()) {
			if ($serv->get_sid()) {			
				$objects = $serv->get_objects();
				if($objects){
					foreach ($objects as $obj) //в массиве объектов ищем имя сервера
					{
						if ($obj['class'] == 'Server')
							{
								$serv->status['name']= $obj['name'];	
							}
					}
				}			
					echo '<div class = "trassir_server_name">';	//вывод имени сервера
						echo  $serv->status['name'];	
					echo '</div>';
					echo '<div class = "trassir_server_status">';	//вывод здоровья
						$health = $serv->get_health();
						foreach ($health as $key => $value){
							if (($key == 'disks' || $key == 'database' || $key == 'network' || $key == 'automation')&& $value=='1') 
								{
									echo '<div class = "OK">';
									echo $key . ': ';
									echo 'OK';
									echo '</div>';
								}
								else if ($key == 'cpu_load' && $value <= 75)
								{
									echo '<div class = "OK">';
									echo 'Загрузка ЦП: ';
									echo $value . '%';
									echo '</div>';			
								}	
								else if ($key == 'uptime' && $value > 3600)
								{
									echo '<div class = "OK">';
									echo 'Аптайм: ';				
										$day = floor($value/86400);				
										$value1 = $value - $day*86400;
										$hour = floor(($value - $day*86400)/3600);
										echo $day.' days '.$hour . ' hours';
									echo '</div>';			
								}	
								else if ($key == 'uptime' && $value <= 3600)
								{
									echo '<div class = "error">';
									echo $key . ' less than hour : ';
									echo $value . ' seconds';
									echo '</div>';			
								}			
								else if ($key == 'channels_total')
								{
									echo '<div class = "OK">';
									echo 'Каналов всего: ';
									echo $value;
									$ch_total = $value;
									echo '</div>';			
								}	
								else if ($key == 'channels_online' && $value == $ch_total)
								{
									echo '<div class = "OK">';
									echo 'Каналов онлайн: ';
									echo $value;
									$ch_total = $value;
									echo '</div>';	
								}		
								else if (($key == 'disks_stat_main_days' || $key == 'disks_stat_subs_days')&& $value > 45)
								{
									echo '<div class = "OK">';
									echo $key . ': ';
									echo $value;
									echo '</div>';			
								}
								else if ( $key == 'disks_stat_priv_days')
								{
									echo '<div class = "OK">';
									echo $key . ': ';
									echo $value;
									echo '</div>';
								}
								else 
								{
									echo '<div class = "error">';
									echo $key . ': ';
									echo $value;
									echo '</div>';
								}	
						}
					echo '</div>';			
			}
		}
		if (!$serv->status['online'] || !$serv->status['sid'] || !$objects){ //если не прошло соединение, не получена сессия или не считаны объекты сервера выводим ошибку.
			echo '<div class = "error">';	
				echo 'server ' . $ip . '</br>';
				echo 'connection error';
			echo '</div>';	
		}
	echo '</div>';
}


$list_of_servers = fopen("conf/list_of_servers.txt", "r");
if ($list_of_servers) {
    while (($buffer = fgets($list_of_servers)) !== false) {
		trassir_server_monitor($buffer, $user, $password, $sdk_password);
    }
}
fclose($list_of_servers);
echo '<br/>';

?>
</body>
</html>
Результат в моем случае (на период тестирования всего 4 сервера): image В планах:
  1. Автоматический регулярный запуск скрипта (пару раз в сутки) и сохранение статистики состояния серверов в БД.
  2. Возможно централизованное управление УЗ пользователей, если будет актуально к тому моменту когда смогу реализовать первое.
Если есть еще «счастливые» администраторы данной системы, буду рад помощи при желании совместной доработки функционала. Так же если кому-то интересен итоговый результат, но разработкой заниматься не готов — пишите на denis.glushakov@bk.ru, постараюсь про вас не забыть. Советы по оптимизации кода с радостью приму в комментариях. UPD от 22.06.2018. Так как некоторый интерес к материалу все же был, сделаю небольшой апдейт: Переписал весь этот ужас в более-менее приличную либу доступную на packagist (ищите по слову Trassir) и сделал на симфони в иной реализации с полным функционалом (сохранение состояний в БД, отображения статистики и т.д.). Кому актуально — пишите на почту, времени найти чтобы опубликовать пока нету, да и не дописано пока до конца.