Все начиналось, с некорректно работающее оборудки которая не понимала SNMP запросы, и работать надо было через telnet команды, ну так начнем, для работы с telnet я использовал сторонии класс , честно говор не помню где его скачал, думаю наити что то похожее не проблема

require_once(__DIR__ . '/TelnetClient/TelnetClient.php');

Добавил наши настройки в самый верх файла это будет очень удобно, укажем такие параметры как логин пароль хост к базе данных биллинга гидры (Oracle DB).

Функция Start , будет выполнять запуск второстепенных функции , которые выполнят наш функционал по включения\отключению абонентов в зависимости от положительного баланса или отрицательного

function Start($conf)
{

  $filelog = new FileLog($conf);
  $mac_on_really = [];
  $mac_off_really = [];

  $hydra = new HydraAPI($conf);
  $mac_on =  $hydra->Get_BDCOM_ONOFF(true);
  $mac_off =  $hydra->Get_BDCOM_ONOFF(false);


   $lines = file($this->conf['bdcom_onoff_file'], FILE_IGNORE_NEW_LINES);        


  foreach ($mac_on as $mac) {
    foreach ($lines as $line) {
      $finded = false;
      // Ищем наш мак с строке результата
      if(strpos($line, $mac)>-1)
          {
            if(trim($line) === trim("$mac:enable")) { $finded = true; break;  }
          }
    }

    if($finded === false) { $mac_on_really[] = $mac; }
  }


    foreach ($mac_off as $mac) {
    foreach ($lines as $line) {
      $finded = false;
      // Ищем наш мак с строке результата
      if(strpos($line, $mac)>-1)
          {
            if(trim($line) === trim("$mac:disable")) { $finded = true; break;  }
          }
    }

    if($finded === false) { $mac_off_really[] = $mac; }
  }


  TelnetOnOFFBDCOM($conf,$mac_on_really, true);
  TelnetOnOFFBDCOM($conf,$mac_off_really, false);

}

Функция которая будет включать\выключать устройства BDCOM , создадим объекты классов такие как Filelog - работа с логами, TelnetClient - работа с telnet, используя команды и список мак адресов которые нужно включить\отключить выполним нужные команды запишем результат в файл логов

// Функция которая будет вкл/выкл бдком и записывать значения в фаил
function TelnetOnOFFBDCOM($conf,$mac_arr,$onoff = true)
{
$epon_port = false;
$result_onoff = false;

$filelog = new FileLog($conf);

$telnet = new TelnetClient($host, $port);
$telnet->connect();
// Логин
$telnet->setPrompt('/(ogin|name|word|UserName):.*$/');
$telnet->sendCommand('netadmin', true);
// пароль
$telnet->setPrompt('/(word|PassWord):.*$/');
$telnet->sendCommand('netadmin', true);
$telnet->setPrompt('>');
$telnet->sendCommand('enable', true);
$telnet->setPrompt('#');
$telnet->sendCommand('config', true);
$telnet->setPrompt('#');

if($onoff) $command = "enable" else $command = "disable";

foreach ($mac_arr as $mac_item) {
  // конвертируем мак в нужный формат
  $mac = ConvertMacToDelimitier($mac_item,'.');

$telnet->sendCommand('show epon onu-information mac-address '.$mac, true);
$telnet->setPrompt('registered');
// получаем ответ ввиде массива для каждои строки
$array = $telnet->exec('', true);

foreach($array as $item)
{  // если в строке есть искомый мак
  if(strpos($item,$mac)>-1)
  {
    // если есть второе соотвествие
  if(strpos($item,'EPON')>-1)
    {
    $epon_port_arr = explode(':',$item);
    // получаем EPON порт
    $epon_port = trim($epon_port_arr[0]).':'.trim(substr($epon_port_arr[1], 0,3));
    break;
    }
  }

} // конец перебора ответа

$telnet->sendCommand("", true);

if($epon_port) 
{
  $port = str_replace("EPON",'',$epon_port);
  $telnet->sendCommand("interface epON ".$port);
  $telnet->setPrompt('#');
  $telnet->sendCommand("epon onu catv $command", true);
  $telnet->setPrompt('/');
  $result_onoff = true;

}
// если результат успешно то запишем результат в фаил
if($result_onoff) { 
  $str = ; 
   // ищем результат в фаиле
   $data = $filelog->GetStrFromFile($mac);
   if($data)
   {
   $result =  $data['data'];
   $line = $data['line'];
   // делаем замену строки
   $filelog->SetReplaceStr($line, $mac.':'.$command);
   }
   else
   {
    // добавляем новую строку
    $filelog->SetRewriteFile($mac.':'.$command);  
   }
   $log_str = date("Y-m-d h:i:s")." mac ".$mac.':'.$command;
   $filelog->SetRewriteLogFile($log_str);
   
}

} // перебор нескольких мак адресов

$telnet->sendCommand('exit', true);

$telnet->disconnect();

}

Напишем класс который будет работать с логами.

class FileLog
{
   public $conf;

   function __construct($conf)
   {
    $this->conf = $conf;
   }


   function SetRewriteLogFile($str)
   {
    $fhandle = fopen($this->conf['log_file_name'],"w");
    fwrite($fhandle,$content);
    fclose($fhandle);
   }

   function SetRewriteFile($str)
   {
    $fhandle = fopen($this->conf['bdcom_onoff_file'],"w");
    fwrite($fhandle,$content);
    fclose($fhandle);
   }

   function SetAddStringFile($str)
   {
    $fp = fopen($this->conf['log_file_name'], 'a+'); //- а+ добавляет новые данные после существующих
    fwrite($fp, $str."\r\n"); //а "r\n\" - вставляет их каждый рас с новой строки
    fclose($fp);
   }

   // Прочитать строку в которои есть этот мак
   function GetStrFromFile($mac)
   {    
            $result = false;
            $line = 0;
            $fp = @fopen($this->conf['bdcom_onoff_file'], "r");
        if ($fp) {
            while (($buffer = fgets($fp, 4096)) !== false) {
               if(strpos($buffer, $mac) !== false) {  $result['data'] = explode(':', $buffer); $result['line'] = $line; break;  }
               $line = $line +1;
            }           

            if (!feof($fp)) {
                echo "Ошибка: fgets() неожиданно потерпел неудачу\n";
            }
            fclose($fp);
        }
        return $result;
   }

Напишем класс который будет работать с биллингом Hydra (Oracle DB), наверное единственная функция в классе которая будет иметь интерес Get_BDCOM_ONOFF - позволяет используя SQL запрос получить нам должников или абонентов с положительным балансом.

class HydraAPI
{
	 public $conn;

 function __construct($conf)
    {
        try {
            $conn = @oci_connect($conf['hydra_user'], $conf['hydra_pwd'], $conf['hydra_host'] . '/' . $conf['hydra_db'], 'AL32UTF8');
            if (!$conn) {
                $e = oci_error();
               // trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);
            }

            $this->conn = $conn;
        }catch(Exception $e) {}

    }




public function Get_BDCOM_ONOFF($onoff = true)
    {
        try{
            
            if($onoff) { $balance_str = '>'; } else { $balance_str = '<'; }

            $query = "SELECT DISTINCT
CONCAT('https://95.182.107.243:8000/subjects/persons/edit/',OS.N_SUBJECT_ID) URL,
       SU.VC_BASE_SUBJECT_NAME,
       OV.VC_VALUE,        -- Значение 
       g.vc_name,
       SU.N_SUBJ_STATE_ID
FROM   SI_V_OBJ_SUBJECTS   OS,
si_subj_accounts sa,
SI_V_USERS SU,
       SR_GOODS G,
       SI_OBJ_OBJECTS SOO,
       SI_V_OBJECTS_SIMPLE O 
    LEFT JOIN SI_V_OBJ_VALUES  OV on (OV.N_GOOD_VALUE_TYPE_ID in (55898801,56112401) AND  O.N_OBJECT_ID = OV.N_OBJECT_ID  )   
WHERE  OS.N_OBJECT_ID  = O.N_OBJECT_ID 
AND    g.n_good_id = o.n_good_id
AND SU.N_CUSTOMER_ID = OS.N_SUBJECT_ID
AND SU.N_SUBJ_STATE_ID = 2011
AND sa.n_subject_id= os.n_subject_id
AND SI_USERS_PKG_S.GET_ACCOUNT_BALANCE_SUM(sa.n_account_id) $balance_str 0
AND OV.VC_VALUE IS NOT NULL
and exists (Select * from si_v_subscriptions ss where ss.n_customer_id = su.n_customer_id and ss.n_service_id in (54614801) and ss.c_fl_closed='N'  )
AND soo.n_object_id = o.n_object_id  
AND soo.n_bind_object_id in (SELECT obj2.n_object_id FROM SI_V_OBJECTS OBJ2 WHERE obj2.n_good_id=56105201 and obj2.n_object_id = 56107601)";
        

         $stid = oci_parse($this->conn, $query);
            oci_execute($stid);

            while ($row = oci_fetch_array($stid, OCI_ASSOC + OCI_RETURN_NULLS)) {
             $MAC = $row['VC_VALUE'];
              $result[] = $MAC;
            }

                return $result;

            }catch (\Exception $e){ return false;}


    }



}

Комментарии (9)


  1. martin74ua
    31.08.2022 17:54

    Убожество...
    Управление доступом абонента - это не задача сети доступа, это задача браса.

    Далее, зачем постоянно искать абонентов? Вы что, не знаете куда он подключен????


    1. GreekIgor Автор
      31.08.2022 18:27

      Можно было конечно это решить и через брас, но к сожалению я не имею к нему доступа, а в чем заключается поиск абонентов - это вы имеете ввиду поиск по мак адресу порта абонента или же перебор абонентов на отключение\включение - если этот вопрос касается абонентов которых перебирает скрипт - то более простого решения я не нашел


      1. martin74ua
        31.08.2022 23:14

        Если стоит задача отключить пользователя - то мы знаем, что это за пользователь. И знаем, что он включен в epon0/1:34. Зачем его еще как то искать???
        Дальше. Ну хорошо. Выключили вы его. А как теперь бедному отключенному пользователю попасть в личный кабинет и узнать, что он вам денег то должен? Или пусть звонит?

        Не имеете доступа к брасу - пусть отключением занимаются те, кто имеет... Им достаточно один COA послать и все произойдет....


        1. 4lex
          01.09.2022 08:15

          Если стоит задача отключить пользователя - то мы знаем, что это за пользователь. И знаем, что он включен в epon0/1:34. Зачем его еще как то искать???

          Не всегда, sub-интерфейс onu-шки может измениться при перезагрузке BDCom (если не записать конфиг когда она забиндилась) или же намеренно перебиндить ее...


      1. martin74ua
        31.08.2022 23:16

        Еще и телнетом на голову ходить... Ей от этого больно. Минимизируйте работу с головой по телнету, лучше по снмп прочитать все, что надо...


    1. arpnet
      01.09.2022 06:49

      Не соглашусь от части с Вами, у каждого сеть настроена по своему, и бывает необходимо глушить абонента на уровне сети, т.к. до самого браса может быть настроен доступ например к коллекции медиатики, по хорошему считаю глушить и на брасе и на порту, но я бы подумал в сторону acl чтобы был доступ например к биллингу и сайту компании.


      1. GreekIgor Автор
        01.09.2022 06:53

        Я не особо разбираюсь в acl в брассах и тп. , но было время когда я связывал старый биллинг с радиусом и писал команды в брас которые обрывали соединение, но потом купили новый биллинг и тут уже доступ был ограничен на нет, и данный пример можно сказать просто костыль в систему в которую уже нельзя как то модифицировать или дополнять


  1. joffer
    31.08.2022 19:58
    +1

    ощущение, что это перевод какой-то записи со StackOverFlow)

    текст бы вычитать, на ошибки проверить, придать какой-то структурности и объяснить, что делаем, зачем и почему так или нет - после этого скорее всего станет ясно, что для такой аудитории как "Хабр" статья не то чтобы... "слабая"... охохо, в общем, этому материалу в публикацию было сильно рано

    п.с: так вот из-за каких статей до сих пор пишут "пхп мёртв")


    1. GreekIgor Автор
      01.09.2022 06:56

      Неа , это не перевод статьи, статья не является каким то ярким примером для подражания, это просто статья начинающего хабаровчанина, так что попрошу сильно ногами не пинать, по возможности и дальнейшему росту буду уже выкладывать более интересные и познавательные статьи