Add router management functionality with GenieACS integration
This commit is contained in:
471
lib/GenieACS/GenieACS.php
Normal file
471
lib/GenieACS/GenieACS.php
Normal file
@@ -0,0 +1,471 @@
|
||||
<?php
|
||||
|
||||
class GenieACS {
|
||||
private $log;
|
||||
private $baseurl;
|
||||
private $username;
|
||||
private $password;
|
||||
private $jwt_token;
|
||||
|
||||
public function __construct($baseurl, $username, $password) {
|
||||
$this->log = mfLoghandler::singleton();
|
||||
|
||||
$this->baseurl = rtrim($baseurl, '/');
|
||||
$this->username = $username;
|
||||
$this->password = $password;
|
||||
|
||||
if (!$this->baseurl || !$this->username || !$this->password) {
|
||||
throw new Exception("Invalid Arguments");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate and retrieve JWT token
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
private function _authenticate() {
|
||||
$session_key = "genieacs.{$this->baseurl}.jwt";
|
||||
$session = new mfConfig($session_key);
|
||||
|
||||
// Check if we have a valid cached token (valid for 1 hour)
|
||||
if ($session->value() && (time() - $session->edit) < 3600) {
|
||||
$this->jwt_token = $session->value();
|
||||
return true;
|
||||
}
|
||||
|
||||
$url = $this->baseurl . '/login';
|
||||
|
||||
$ctx_options = [
|
||||
"http" => [
|
||||
"ignore_errors" => true,
|
||||
"method" => "POST",
|
||||
"header" => [
|
||||
"Accept: application/json, text/*",
|
||||
"Content-Type: application/json; charset=UTF-8",
|
||||
],
|
||||
"content" => json_encode([
|
||||
"username" => $this->username,
|
||||
"password" => $this->password,
|
||||
]),
|
||||
]
|
||||
];
|
||||
|
||||
$ctx = stream_context_create($ctx_options);
|
||||
$response = file_get_contents($url, false, $ctx);
|
||||
|
||||
// Extract JWT from response headers
|
||||
if (isset($http_response_header)) {
|
||||
foreach ($http_response_header as $header) {
|
||||
if (stripos($header, 'set-cookie') !== false && stripos($header, 'genieacs-ui-jwt=') !== false) {
|
||||
preg_match('/genieacs-ui-jwt=([^;]+)/', $header, $matches);
|
||||
if (isset($matches[1])) {
|
||||
$this->jwt_token = $matches[1];
|
||||
|
||||
// Cache the token
|
||||
$session->value($this->jwt_token);
|
||||
$session->save();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("Authentication failed - could not retrieve JWT token");
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a GET request to the API
|
||||
* @param string $endpoint
|
||||
* @return array|null
|
||||
* @throws Exception
|
||||
*/
|
||||
private function _get($endpoint) {
|
||||
if (!$this->jwt_token) {
|
||||
$this->_authenticate();
|
||||
}
|
||||
|
||||
$url = $this->baseurl . $endpoint;
|
||||
|
||||
$ctx_options = [
|
||||
'http' => [
|
||||
'ignore_errors' => true,
|
||||
'method' => 'GET',
|
||||
'header' => [
|
||||
'Cookie: genieacs-ui-jwt=' . $this->jwt_token,
|
||||
'Accept: application/json',
|
||||
],
|
||||
]
|
||||
];
|
||||
|
||||
$ctx = stream_context_create($ctx_options);
|
||||
$response = file_get_contents($url, false, $ctx);
|
||||
|
||||
// Check if we got a 401 and need to re-authenticate
|
||||
if (isset($http_response_header)) {
|
||||
foreach ($http_response_header as $header) {
|
||||
if (stripos($header, 'HTTP/') === 0 && stripos($header, '401') !== false) {
|
||||
// Token expired, re-authenticate and retry
|
||||
$this->jwt_token = null;
|
||||
$this->_authenticate();
|
||||
return $this->_get($endpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return json_decode($response, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a POST request to the API
|
||||
* @param string $endpoint
|
||||
* @param array $data
|
||||
* @return array|null
|
||||
* @throws Exception
|
||||
*/
|
||||
private function _post($endpoint, $data) {
|
||||
if (!$this->jwt_token) {
|
||||
$this->_authenticate();
|
||||
}
|
||||
|
||||
$url = $this->baseurl . $endpoint;
|
||||
$jsonData = json_encode($data);
|
||||
|
||||
$ctx_options = [
|
||||
'http' => [
|
||||
'ignore_errors' => true,
|
||||
'method' => 'POST',
|
||||
'header' => [
|
||||
'Cookie: genieacs-ui-jwt=' . $this->jwt_token,
|
||||
'Accept: application/json',
|
||||
'Content-Type: application/json',
|
||||
'Content-Length: ' . strlen($jsonData)
|
||||
],
|
||||
'content' => $jsonData,
|
||||
]
|
||||
];
|
||||
|
||||
$ctx = stream_context_create($ctx_options);
|
||||
$response = file_get_contents($url, false, $ctx);
|
||||
|
||||
// Log for debugging
|
||||
error_log("GenieACS POST to $url: " . $jsonData);
|
||||
error_log("GenieACS response: " . ($response ?: 'empty'));
|
||||
|
||||
// Check if we got a 401 and need to re-authenticate
|
||||
if (isset($http_response_header)) {
|
||||
foreach ($http_response_header as $header) {
|
||||
error_log("GenieACS response header: " . $header);
|
||||
if (stripos($header, 'HTTP/') === 0 && stripos($header, '401') !== false) {
|
||||
// Token expired, re-authenticate and retry
|
||||
$this->jwt_token = null;
|
||||
$this->_authenticate();
|
||||
return $this->_post($endpoint, $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If response is empty or false, it might still be successful (204 No Content)
|
||||
if ($response === false || $response === '') {
|
||||
// Check if status code indicates success
|
||||
if (isset($http_response_header)) {
|
||||
foreach ($http_response_header as $header) {
|
||||
if (stripos($header, 'HTTP/') === 0) {
|
||||
if (stripos($header, '200') !== false || stripos($header, '202') !== false || stripos($header, '204') !== false) {
|
||||
return ['success' => true];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return json_decode($response, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all devices
|
||||
* @return array|null
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getDevices() {
|
||||
return $this->_get('/api/devices');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific device by ID
|
||||
* @param string $deviceId Device ID (will be URL-encoded automatically)
|
||||
* @return array|null
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getDevice($deviceId) {
|
||||
return $this->_get('/api/devices/' . rawurlencode($deviceId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a task for a device
|
||||
* @param string $deviceId Device ID (will be URL-encoded automatically)
|
||||
* @param array $tasks Array of tasks to execute
|
||||
* @return array|null
|
||||
* @throws Exception
|
||||
*
|
||||
* Example:
|
||||
* $tasks = [
|
||||
* ["name" => "getParameterValues", "parameterNames" => ["InternetGatewayDevice.User.1.Username"]]
|
||||
* ];
|
||||
*/
|
||||
public function createTask($deviceId, $tasks) {
|
||||
return $this->_post('/api/devices/' . rawurlencode($deviceId) . '/tasks', $tasks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parameter values from a device
|
||||
* @param string $deviceId URL-encoded device ID
|
||||
* @param array $parameterNames Array of parameter names to retrieve
|
||||
* @return array|null
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getParameterValues($deviceId, $parameterNames) {
|
||||
$tasks = [
|
||||
[
|
||||
"name" => "getParameterValues",
|
||||
"parameterNames" => $parameterNames
|
||||
]
|
||||
];
|
||||
return $this->createTask($deviceId, $tasks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set parameter values on a device
|
||||
* @param string $deviceId URL-encoded device ID
|
||||
* @param array $parameterValues Array of parameter name => value pairs
|
||||
* @return array|null
|
||||
* @throws Exception
|
||||
*/
|
||||
public function setParameterValues($deviceId, $parameterValues) {
|
||||
$tasks = [
|
||||
[
|
||||
"name" => "setParameterValues",
|
||||
"parameterValues" => $parameterValues
|
||||
]
|
||||
];
|
||||
return $this->createTask($deviceId, $tasks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh device information
|
||||
* @param string $deviceId URL-encoded device ID
|
||||
* @return array|null
|
||||
* @throws Exception
|
||||
*/
|
||||
public function refreshDevice($deviceId) {
|
||||
$tasks = [
|
||||
[
|
||||
"name" => "refreshObject",
|
||||
"objectName" => ""
|
||||
]
|
||||
];
|
||||
return $this->createTask($deviceId, $tasks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reboot a device
|
||||
* @param string $deviceId URL-encoded device ID
|
||||
* @return array|null
|
||||
* @throws Exception
|
||||
*/
|
||||
public function rebootDevice($deviceId) {
|
||||
$tasks = [
|
||||
[
|
||||
"name" => "reboot"
|
||||
]
|
||||
];
|
||||
return $this->createTask($deviceId, $tasks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory reset a device
|
||||
* @param string $deviceId URL-encoded device ID
|
||||
* @return array|null
|
||||
* @throws Exception
|
||||
*/
|
||||
public function factoryResetDevice($deviceId) {
|
||||
$tasks = [
|
||||
[
|
||||
"name" => "factoryReset"
|
||||
]
|
||||
];
|
||||
return $this->createTask($deviceId, $tasks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ping an IP address
|
||||
* @param string $ip IP address to ping
|
||||
* @return array|null
|
||||
* @throws Exception
|
||||
*
|
||||
* Returns: {
|
||||
* "packetsTransmitted": 3,
|
||||
* "packetsReceived": 3,
|
||||
* "packetLoss": 0,
|
||||
* "min": 2.674,
|
||||
* "avg": 3.054,
|
||||
* "max": 3.34,
|
||||
* "mdev": 0.28
|
||||
* }
|
||||
*/
|
||||
public function ping($ip) {
|
||||
return $this->_get('/api/ping/' . $ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a file from a device
|
||||
* @param string $deviceId URL-encoded device ID
|
||||
* @param string $fileType File type to download
|
||||
* @param string $fileName Optional file name
|
||||
* @return array|null
|
||||
* @throws Exception
|
||||
*/
|
||||
public function downloadFile($deviceId, $fileType, $fileName = null) {
|
||||
$task = [
|
||||
"name" => "download",
|
||||
"fileType" => $fileType
|
||||
];
|
||||
|
||||
if ($fileName) {
|
||||
$task["fileName"] = $fileName;
|
||||
}
|
||||
|
||||
return $this->createTask($deviceId, [$task]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse device data from API response to extract useful information
|
||||
* @param array $deviceData Raw device data from API
|
||||
* @return array Parsed device information
|
||||
*/
|
||||
public static function parseDeviceData($deviceData) {
|
||||
$parsed = [];
|
||||
|
||||
foreach ($deviceData as $key => $value) {
|
||||
// Extract simple values
|
||||
if (isset($value['value']) && is_array($value['value'])) {
|
||||
$parsed[$key] = $value['value'][0];
|
||||
}
|
||||
}
|
||||
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get device ID from device data
|
||||
* @param array $deviceData Raw device data from API
|
||||
* @return string|null Device ID
|
||||
*/
|
||||
public static function getDeviceId($deviceData) {
|
||||
if (isset($deviceData['DeviceID.ID']['value'][0])) {
|
||||
return $deviceData['DeviceID.ID']['value'][0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get MAC address from device data
|
||||
* @param array $deviceData Raw device data from API
|
||||
* @return string|null MAC address
|
||||
*/
|
||||
public static function getMacAddress($deviceData) {
|
||||
// Try WAN connection MAC address first
|
||||
if (isset($deviceData['InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.MACAddress']['value'][0])) {
|
||||
return $deviceData['InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.MACAddress']['value'][0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get external IP address from device data
|
||||
* @param array $deviceData Raw device data from API
|
||||
* @return string|null External IP address
|
||||
*/
|
||||
public static function getExternalIP($deviceData) {
|
||||
// Try to get from WAN IP Connection
|
||||
if (isset($deviceData['InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress']['value'][0])) {
|
||||
return $deviceData['InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress']['value'][0];
|
||||
}
|
||||
|
||||
// Try alternative connection
|
||||
if (isset($deviceData['InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.2.ExternalIPAddress']['value'][0])) {
|
||||
return $deviceData['InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.2.ExternalIPAddress']['value'][0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get management/local IP address from device data (private IP)
|
||||
* @param array $deviceData Raw device data from API
|
||||
* @return string|null Management IP address
|
||||
*/
|
||||
public static function getManagementIP($deviceData) {
|
||||
// Check both WAN connections and return the one with a private IP
|
||||
$ips = [];
|
||||
|
||||
if (isset($deviceData['InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress']['value'][0])) {
|
||||
$ips[] = $deviceData['InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress']['value'][0];
|
||||
}
|
||||
|
||||
if (isset($deviceData['InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.2.ExternalIPAddress']['value'][0])) {
|
||||
$ips[] = $deviceData['InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.2.ExternalIPAddress']['value'][0];
|
||||
}
|
||||
|
||||
// Return the first private IP found (10.x.x.x, 172.16-31.x.x, 192.168.x.x)
|
||||
foreach ($ips as $ip) {
|
||||
if (self::isPrivateIP($ip)) {
|
||||
return $ip;
|
||||
}
|
||||
}
|
||||
|
||||
// If no private IP found, return first IP
|
||||
return $ips[0] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an IP address is in a private range
|
||||
* @param string $ip IP address
|
||||
* @return bool True if private IP
|
||||
*/
|
||||
private static function isPrivateIP($ip) {
|
||||
$parts = explode('.', $ip);
|
||||
if (count($parts) !== 4) return false;
|
||||
|
||||
$first = (int)$parts[0];
|
||||
$second = (int)$parts[1];
|
||||
|
||||
// 10.0.0.0 - 10.255.255.255
|
||||
if ($first === 10) return true;
|
||||
|
||||
// 172.16.0.0 - 172.31.255.255
|
||||
if ($first === 172 && $second >= 16 && $second <= 31) return true;
|
||||
|
||||
// 192.168.0.0 - 192.168.255.255
|
||||
if ($first === 192 && $second === 168) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get device manufacturer, model, and version info
|
||||
* @param array $deviceData Raw device data from API
|
||||
* @return array Device info
|
||||
*/
|
||||
public static function getDeviceInfo($deviceData) {
|
||||
return [
|
||||
'manufacturer' => $deviceData['DeviceID.Manufacturer']['value'][0] ?? null,
|
||||
'productClass' => $deviceData['DeviceID.ProductClass']['value'][0] ?? null,
|
||||
'oui' => $deviceData['DeviceID.OUI']['value'][0] ?? null,
|
||||
'serialNumber' => $deviceData['DeviceID.SerialNumber']['value'][0] ?? null,
|
||||
'hardwareVersion' => $deviceData['InternetGatewayDevice.DeviceInfo.HardwareVersion']['value'][0] ?? null,
|
||||
'softwareVersion' => $deviceData['InternetGatewayDevice.DeviceInfo.SoftwareVersion']['value'][0] ?? null,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user