trafficmonitor/app/Services/CollectorService.php
2025-11-10 16:34:21 +09:00

169 lines
7.2 KiB
PHP

<?php
namespace App\Services;
use App\DTOs\CollectorDTO;
use App\Entities\CollectorEntity;
use App\Forms\CollectorForm;
use App\Helpers\CollectorHelper;
use App\Models\CollectorModel;
use CodeIgniter\I18n\Time;
use RuntimeException;
class CollectorService extends CommonService
{
const OID_IF_IN_OCTETS = '1.3.6.1.2.1.2.2.1.10.'; // ifInOctets (Raw Octets)
const OID_IF_OUT_OCTETS = '1.3.6.1.2.1.2.2.1.16.'; // ifOutOctets (Raw Octets)
const SNMP_VERSION = '2c';
const SNMP_COMMNUNITY = 'IDC-JP';
public function __construct(CollectorModel $model)
{
parent::__construct($model);
$this->addClassPaths('Collector');
}
public function getFormService(): CollectorForm
{
if ($this->formServiceInstance === null) {
$this->formServiceInstance = new CollectorForm();
$this->formServiceInstance->setAttributes([
'pk_field' => $this->model->getPKField(),
'title_field' => $this->model->getTitleField(),
'table' => $this->model->getTable(),
'useAutoIncrement' => $this->model->useAutoIncrement(),
'class_path' => $this->getClassPaths(false)
]);
}
return $this->formServiceInstance;
}
public function getHelper(): CollectorHelper
{
if ($this->helperInstance === null) {
$this->helperInstance = new CollectorHelper();
$this->helperInstance->setAttributes([
'pk_field' => $this->model->getPKField(),
'title_field' => $this->model->getTitleField(),
'table' => $this->model->getTable(),
'useAutoIncrement' => $this->model->useAutoIncrement(),
'class_path' => $this->getClassPaths(false)
]);
}
return $this->helperInstance;
}
//기본 기능부분
protected function create_process(array $formDatas): CollectorEntity
{
//CollectorEntity를 생성하면 Setter가 자동 호출됩니다.
return new CollectorEntity($formDatas);
}
public function create(object $dto): CollectorEntity
{
if (!$dto instanceof CollectorDTO) {
throw new RuntimeException(__METHOD__ . "에서 오류발생:" . get_class($dto) . "는 사용할수 없습니다.");
}
return parent::create($dto);
}
protected function modify_process($uid, array $formDatas): CollectorEntity
{
if (!$uid) {
throw new \Exception("수집 번호가 정의 되지 않았습니다.");
}
$entity = $this->getEntity($uid);
if (!$entity instanceof CollectorEntity) {
throw new \Exception("{$uid}에 해당하는 수집정보을 찾을수 없습니다.");
}
// 변경 사항을 Entity에 적용합니다. (Dirty Tracking 활성화)
$formDatas[$this->model->getPKField()] = $uid;
$entity->fill($formDatas);
// 💡 부모 호출 제거: 변경된 Entity 객체를 반환합니다.
return $entity;
}
public function modify($uid, object $dto): CollectorEntity
{
if (!$dto instanceof CollectorDTO) {
throw new RuntimeException(__METHOD__ . "에서 오류발생:" . get_class($dto) . "는 사용할수 없습니다.");
}
return parent::modify($uid, $dto);
}
//List 검색용
//FormFilter 조건절 처리
//검색어조건절처리
public function setSearchWord(string $word): void
{
$this->model->orLike($this->model->getTable() . "." . $this->model->getTitleField(), $word, 'both');
parent::setSearchWord($word);
}
/**
* SNMP GET 요청을 실행하고 Octet 값을 추출합니다.
* @param string $ip 장비 IP
* @param int $ifIndex 인터페이스 인덱스
* @param string $community SNMP 커뮤니티 문자열
* @param string $oid OID (접두사)
* @return int|null 수집된 Octet 값 또는 오류 시 null
*/
protected function getOctets(string $ip, int $ifIndex, string $community, string $oid): ?int
{
$fullOid = $oid . $ifIndex;
// snmp2_get을 사용하여 SNMP v2c로 요청 (64비트 카운터 지원에 유리)
$result = @snmp2_get($ip, $community, $fullOid, 100000, 3); // 3초 타임아웃
if ($result === false || $result === null) {
log_message('error', "SNMP 통신 실패: {$ip} ({$fullOid}, Community: {$community})");
return null;
}
// 결과 문자열에서 숫자 값만 추출하여 정수로 변환
if (preg_match('/\d+/', $result, $matches)) {
return (int)$matches[0];
}
return null;
}
/**
* 트래픽을 수집하고 Kbit/s를 계산하여 DB에 저장합니다.
* @param int $trafficInfoUid traffic_info 테이블의 기본 키 (연계 키)
* @param string $ip 장비 IP
* @param int $ifIndex 인터페이스 인덱스
* @param string $community SNMP 커뮤니티 문자열
*/
public function collectAndCalculate(int $trafficInfoUid, string $ip, int $ifIndex, string $community): void
{
$currentInOctets = $this->getOctets($ip, $ifIndex, $community, self::OID_IF_IN_OCTETS);
$currentOutOctets = $this->getOctets($ip, $ifIndex, $community, self::OID_IF_OUT_OCTETS);
$currentTime = Time::now()->toDateTimeString();
if ($currentInOctets === null || $currentOutOctets === null) {
log_message('warning', "트래픽 수집 실패: {$ip} - IF{$ifIndex} (UID: {$trafficInfoUid})");
return;
}
// 이전 데이터를 조회하여 Rate 계산에 사용
$lastEntry = $this->model->getLastEntryByInfoUid($trafficInfoUid);
$inKbitsSec = 0.0;
$outKbitsSec = 0.0;
// 이전 데이터가 있어야만 Rate 계산 가능
if ($lastEntry !== null) {
$lastTime = Time::parse($lastEntry['created_at'])->getTimestamp();
$deltaTime = Time::now()->getTimestamp() - $lastTime;
if ($deltaTime > 0) {
// Raw Octets 값의 차분 계산
$deltaInOctets = $currentInOctets - $lastEntry['raw_in_octets'];
$deltaOutOctets = $currentOutOctets - $lastEntry['raw_out_octets'];
// Kbit/s 계산: (Delta_Octets * 8 bits) / Delta_Time_Seconds / 1000 (-> Kbit/s)
$inKbitsSec = ($deltaInOctets * 8) / $deltaTime / 1000;
$outKbitsSec = ($deltaOutOctets * 8) / $deltaTime / 1000;
} else {
log_message('error', "시간 차이 오류 발생: {$ip} - {$deltaTime}초 (UID: {$trafficInfoUid})");
}
}
// Collector DB에 결과 저장
$this->model->insert([
'trafficinfo_uid' => $trafficInfoUid,
'in_kbits_sec' => round($inKbitsSec, 2),
'out_kbits_sec' => round($outKbitsSec, 2),
'raw_in_octets' => $currentInOctets, // 다음 계산을 위해 Raw 값 저장
'raw_out_octets' => $currentOutOctets, // 다음 계산을 위해 Raw 값 저장
'created_at' => $currentTime,
]);
log_message('info', "트래픽 계산 및 저장 완료 (UID: {$trafficInfoUid}), In: {$inKbitsSec} Kb/s");
}
}