trafficmonitor init...2

This commit is contained in:
choi.jh 2025-11-11 08:52:20 +09:00
parent 7e9f3e2247
commit 2489c426e2
8 changed files with 115 additions and 50 deletions

View File

@ -6,8 +6,10 @@ class CollectorDTO extends CommonDTO
{
public ?int $uid = null;
public ?string $trafficinfo_uid = null;
public ?string $in = null;
public ?string $out = null;
public ?int $in = null;
public ?int $out = null;
public ?int $raw_in = null;
public ?int $raw_out = null;
public function __construct(array $datas = [])
{
@ -26,6 +28,8 @@ class CollectorDTO extends CommonDTO
'trafficinfo_uid' => $this->trafficinfo_uid,
'in' => $this->in,
'out' => $this->out,
'raw_in' => $this->raw_in,
'raw_out' => $this->raw_out,
];
}
}

View File

@ -15,6 +15,36 @@
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `collectorinfo`
--
DROP TABLE IF EXISTS `collectorinfo`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `collectorinfo` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`trafficinfo_uid` int(11) NOT NULL,
`in` int(11) NOT NULL DEFAULT 0 COMMENT 'IN KBit/SEC',
`out` int(11) NOT NULL DEFAULT 0 COMMENT 'OUT KBit/SEC',
`raw_in` int(11) NOT NULL DEFAULT 0 COMMENT 'RAW IN ',
`raw_out` int(11) NOT NULL DEFAULT 0 COMMENT 'RAW OUT',
`updated_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT current_timestamp(),
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='수집정보';
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `collectorinfo`
--
LOCK TABLES `collectorinfo` WRITE;
/*!40000 ALTER TABLE `collectorinfo` DISABLE KEYS */;
/*!40000 ALTER TABLE `collectorinfo` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `mylog`
--
@ -53,6 +83,7 @@ CREATE TABLE `trafficinfo` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`client` varchar(50) NOT NULL,
`switch` varchar(10) NOT NULL,
`community` varchar(20) NOT NULL DEFAULT 'IDC-JP',
`ip` char(16) NOT NULL,
`interface` varchar(20) NOT NULL,
`status` varchar(20) NOT NULL DEFAULT 'available',
@ -116,4 +147,4 @@ UNLOCK TABLES;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2025-11-10 14:43:07
-- Dump completed on 2025-11-10 17:22:27

View File

@ -4,9 +4,37 @@ namespace App\Entities;
use App\Models\TrafficModel;
/**
* 모니터링 대상 장비/인터페이스 정보를 담는 엔티티
*/
class TrafficEntity extends CommonEntity
{
const PK = TrafficModel::PK;
const TITLE = TrafficModel::TITLE;
//기본기능용
protected $casts = [
// 'role' => 'json-array', // 🚫 CSV 형식 저장을 위해 제거
];
public function getIP(): string
{
return $this->attributes['ip'];
}
public function getInterface(): string
{
return $this->attributes['interface'];
}
public function getCommunity(): string
{
return $this->attributes['community'] ?? 'public';
}
// Setter 예시: IP 주소 설정 시 유효성 검사 또는 로직 추가 가능
public function setIP(string $ip): void
{
// 예시: IP 주소 유효성 검사 로직 추가 가능
if (filter_var($ip, FILTER_VALIDATE_IP) === false) {
throw new \InvalidArgumentException("유효하지 않은 IP 주소입니다.");
}
$this->attributes['ip'] = $ip;
}
}

View File

@ -16,18 +16,14 @@ class UserEntity extends CommonEntity
// 'role' => 'json-array', // 🚫 CSV 형식 저장을 위해 제거
];
// --- Getter Methods ---
public function getID(): string
{
return (string) $this->attributes['id'];
}
public function getPassword(): string
{
return $this->attributes['passwd'];
}
/**
* 사용자의 역할을 배열 형태로 반환합니다.
* DB의 JSON 또는 CSV 형식 데이터를 모두 배열로 복구할 있는 로직을 포함합니다.
@ -36,12 +32,10 @@ class UserEntity extends CommonEntity
public function getRole(): array
{
$role = $this->attributes['role'] ?? null;
// 1. 이미 배열인 경우 (방어적 코딩)
if (is_array($role)) {
return array_filter($role);
}
// 2. 문자열 데이터인 경우 처리
if (is_string($role) && !empty($role)) {
// 2-a. JSON 디코딩 시도 (기존 DB의 JSON 형식 처리)
@ -49,18 +43,15 @@ class UserEntity extends CommonEntity
if (json_last_error() === JSON_ERROR_NONE && is_array($decodedRole)) {
return $decodedRole;
}
// 2-b. JSON이 아니면 CSV로 가정하고 변환
$parts = explode(',', $role);
// 각 요소의 불필요한 공백과 따옴표 제거
$cleanedRoles = array_map(fn($item) => trim($item, " \t\n\r\0\x0B\""), $parts);
return array_filter($cleanedRoles);
}
// 3. 변환에 실패했거나 데이터가 없는 경우 빈 배열 반환
return [];
}
// --- Setter Methods ---
public function setPasswd(string $password)

View File

@ -21,6 +21,8 @@ class CollectorForm extends CommonForm
'trafficinfo_uid',
'in',
'out',
'raw_in',
'raw_out',
];
break;
case 'modify':
@ -30,6 +32,8 @@ class CollectorForm extends CommonForm
'trafficinfo_uid',
'in',
'out',
'raw_in',
'raw_out',
];
break;
case 'view':
@ -38,6 +42,8 @@ class CollectorForm extends CommonForm
'trafficinfo_uid',
'in',
'out',
'raw_in',
'raw_out',
'created_at',
];
break;
@ -47,6 +53,8 @@ class CollectorForm extends CommonForm
'trafficinfo_uid',
'in',
'out',
'raw_in',
'raw_out',
'created_at',
];
break;
@ -63,6 +71,8 @@ class CollectorForm extends CommonForm
case "trafficinfo_uid":
case "in":
case "out":
case "raw_in":
case "raw_out":
$rule = "required|trim|neumeric";
$rules[$field] = $rule;
break;

View File

@ -3,8 +3,10 @@ return [
'title' => "수집정보",
'label' => [
'trafficinfo_uid' => "고객명",
'in' => "IN",
'out' => "OUT",
'in' => "IN Kbit/s",
'out' => "OUT Kbit/s",
'raw_in' => "IN Octets",
'raw_out' => "OUT Octets",
'updated_at' => "수정일",
'created_at' => "생성일",
'deleted_at' => "삭제일",

View File

@ -18,6 +18,8 @@ class CollectorModel extends CommonModel
"trafficinfo_uid",
"in",
"out",
"raw_in",
"raw_out",
"updated_at"
];
public function __construct()

View File

@ -4,6 +4,7 @@ namespace App\Services;
use App\DTOs\CollectorDTO;
use App\Entities\CollectorEntity;
use App\Entities\TrafficEntity;
use App\Forms\CollectorForm;
use App\Helpers\CollectorHelper;
use App\Models\CollectorModel;
@ -15,7 +16,6 @@ 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);
@ -93,54 +93,45 @@ class CollectorService extends CommonService
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
private function getSNMPOctets(TrafficEntity $trafficEntity, string $oid): ?int
{
$fullOid = $oid . $ifIndex;
// snmp2_get을 사용하여 SNMP v2c로 요청 (64비트 카운터 지원에 유리)
$result = @snmp2_get($ip, $community, $fullOid, 100000, 3); // 3초 타임아웃
$fullOid = $oid . $trafficEntity->getInterface();
$community = $trafficEntity->getCommunity();
$ip = $trafficEntity->getIP();
// snmp2_get을 사용하여 SNMP v2c로 요청
// 💡 snmp2_get() 함수가 존재하지 않는다는 LSP 오류를 무시하기 위해 @suppress 태그 사용
/** @phpstan-ignore-next-line */
$result = @snmp2_get($ip, $community, $fullOid, 100000, 3);
if ($result === false || $result === null) {
log_message('error', "SNMP 통신 실패: {$ip} ({$fullOid}, Community: {$community})");
return null;
}
// 결과 문자열에서 숫자 값만 추출하여 정수로 변환
if (preg_match('/\d+/', $result, $matches)) {
// SNMP Counter는 BigInt가 될 수 있으므로 intval 대신 (int) 캐스팅을 사용하여
// 최대한 큰 정수로 변환합니다. PHP 64비트 환경이 필요합니다.
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
public function getCalculatedData(TrafficEntity $trafficEntity): array
{
$currentInOctets = $this->getOctets($ip, $ifIndex, $community, self::OID_IF_IN_OCTETS);
$currentOutOctets = $this->getOctets($ip, $ifIndex, $community, self::OID_IF_OUT_OCTETS);
$currentInOctets = $this->getSNMPOctets($trafficEntity, self::OID_IF_IN_OCTETS);
$currentOutOctets = $this->getSNMPOctets($trafficEntity, self::OID_IF_OUT_OCTETS);
$currentTime = Time::now()->toDateTimeString();
if ($currentInOctets === null || $currentOutOctets === null) {
log_message('warning', "트래픽 수집 실패: {$ip} - IF{$ifIndex} (UID: {$trafficInfoUid})");
return;
$message = "트래픽 수집 실패: {$trafficEntity->getIP()} - IF{$trafficEntity->getInterface()} (UID: {$trafficEntity->getPK()})";
log_message('warning', $message);
throw new \Exception($message);
}
// 이전 데이터를 조회하여 Rate 계산에 사용
$lastEntry = $this->model->getLastEntryByInfoUid($trafficInfoUid);
// 이전 데이터를 조회하여 Rate 계산에 사용 (trafficModel 사용)
$lastEntry = $this->model->getLastEntryByInfoUid($trafficEntity->getPK());
$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'];
@ -150,19 +141,25 @@ class CollectorService extends CommonService
$inKbitsSec = ($deltaInOctets * 8) / $deltaTime / 1000;
$outKbitsSec = ($deltaOutOctets * 8) / $deltaTime / 1000;
} else {
log_message('error', "시간 차이 오류 발생: {$ip} - {$deltaTime}초 (UID: {$trafficInfoUid})");
log_message('error', "시간 차이 오류 발생: {$trafficEntity->getIP()} - {$deltaTime}초 (UID: {$trafficEntity->getPK()})");
}
}
// Collector DB에 결과 저장
$this->model->insert([
'trafficinfo_uid' => $trafficInfoUid,
// DB에 저장할 데이터를 배열로 반환
return [
'trafficinfo_uid' => $trafficEntity->getPK(),
'in_kbits_sec' => round($inKbitsSec, 2),
'out_kbits_sec' => round($outKbitsSec, 2),
'raw_in_octets' => $currentInOctets, // 다음 계산을 위해 Raw 값 저장
'raw_out_octets' => $currentOutOctets, // 다음 계산을 위해 Raw 값 저장
'raw_in_octets' => $currentInOctets,
'raw_out_octets' => $currentOutOctets,
'created_at' => $currentTime,
]);
log_message('info', "트래픽 계산 및 저장 완료 (UID: {$trafficInfoUid}), In: {$inKbitsSec} Kb/s");
];
}
public function collectAndSave(TrafficEntity $trafficEntity): void
{
$data = $this->getCalculatedData($trafficEntity);
// Collector DB에 결과 저장
$this->model->insert($data);
log_message('info', "트래픽 계산 및 저장 완료 (UID: {$trafficEntity->getPK()}), In: {$data['in_kbits_sec']} Kb/s");
}
}