Blog. Just Blog

Удаленное управление питанием сервера через Rack PDU

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Web-мастеру и не только | Автор: ManHunter
Удаленное управление питанием сервера через Rack PDU
Удаленное управление питанием сервера через Rack PDU

Когда-то по работе возникла необходимость автоматизированного управления серверами в стойке дата-центра. Вопрос с контролем за питанием был решен через power-свич (Switched Rack PDU), который имел возможность удаленного управления по telnet. Для эмуляции действий пользователя я написал вот такой небольшой класс на PHP. Может быть пригодится кому-нибудь еще.

Комментариев в коде не очень много, так как все рисовалось под свои нужды, но все должно быть понятно и без подробных описаний. Для каждого действия сперва выясняется "ручная" последовательность нажатий клавиш в консоли, а затем она просто воспроизводится через сокеты.
  1. <?
  2. // Определение команд управления питанием. Подробности
  3. // по каждой команде читайте в документации к устройству
  4.  
  5. define ("IMMEDIATE_ON"1);     // Немедленное включение порта
  6. define ("IMMEDIATE_OFF"2);    // Немедленное выключение порта
  7. define ("IMMEDIATE_REBOOT"3); // Немедленная перезагрузка
  8. define ("DELAYED_ON"4);       // Отложенное включение порта
  9. define ("DELAYED_OFF"5);      // Отложенное выключение порта
  10. define ("DELAYED_REBOOT"6);   // Отложенная перезагрузка
  11.  
  12. class telnet {
  13.  
  14. var $host;
  15. var $port=23;
  16. var $timeout=30;
  17. var $login;
  18. var $password;
  19. var $connection;
  20. var $type;
  21. var $error;
  22. var $errormsg;
  23.  
  24. define ("ESC"chr(27));
  25.  
  26. // Получение списка и состояния всех портов power-свича
  27. public function Port_List() {
  28.   $this->Get_Type();
  29.  
  30.   if ($this->telnet_login()) {
  31.     $this->Put_Command("1");
  32.     $this->Put_Command("2");
  33.  
  34.     if ($this->type==1) {
  35.       $this->Put_Command("1");
  36.     }
  37.     $this->Put_Command("");
  38.  
  39.     $this->Put_Command(ESC,1);
  40.     $this->Put_Command(ESC,1);
  41.  
  42.     if ($this->type==1) {
  43.       $this->Put_Command(ESC,1);
  44.     }
  45.  
  46.     $this->Put_Command("4");
  47.  
  48.     $data=$this->Get_Data();
  49.     if ($data) {
  50.       preg_match_all("/\n([0-9]+)\- (([- A-Za-z0-9\(\)]+ ){1,})".
  51.         " {4,}([ONF]+)/",$data,$matches);
  52.  
  53.       if (count($matches[1])) {
  54.         $tmp=Array();
  55.         for ($i=0$i<count($matches[1]); $i++) {
  56.           $tmp[$matches[1][$i]]=Array(
  57.             "name"=>trim($matches[2][$i]),
  58.             "state"=>$matches[4][$i]
  59.           );
  60.         }
  61.         return $tmp;
  62.       }
  63.       else {
  64.         $this->error=777;
  65.         $this->errormsg="Invalid data recived";
  66.         return false;
  67.       }
  68.     }
  69.     else {
  70.       return false;
  71.     }
  72.   }
  73.   else {
  74.     return false;
  75.   }
  76. }
  77.  
  78. // Получение состояния отдельного порта свича
  79. public function Get_State($port) {
  80.   $this->Get_Type();
  81.  
  82.   if ($this->telnet_login()) {
  83.     $this->Put_Command("1");
  84.     $this->Put_Command("2");
  85.  
  86.     if ($this->type==1) {
  87.       $this->Put_Command("1");
  88.     }
  89.  
  90.     $this->Put_Command("");
  91.  
  92.     $this->Put_Command(ESC,1);
  93.     $this->Put_Command(ESC,1);
  94.  
  95.     if ($this->type==1) {
  96.       $this->Put_Command(ESC,1);
  97.     }
  98.  
  99.     $this->Put_Command("4");
  100.  
  101.     $data=$this->Get_Data();
  102.     if ($data) {
  103.       $pocket=Array();
  104.       ereg("\n".$port."- (([A-Za-z0-9]+ ){1,}) +([A-Z]+)",$data,$pocket);
  105.       if ($pocket[3]!="") {
  106.         return $pocket[3];
  107.       }
  108.       else {
  109.         $this->error=777;
  110.         $this->errormsg="Invalid data recived";
  111.         return false;
  112.       }
  113.     }
  114.     else {
  115.       return false;
  116.     }
  117.  
  118.   }
  119.   else {
  120.     return false;
  121.   }
  122. }
  123.  
  124. // Изменения состояния (включение-выключение) порта
  125. public function Turn_Port($port,$action) {
  126.   $this->Get_Type();
  127.  
  128.   if ($this->telnet_login()) {
  129.     $this->Put_Command("1");
  130.     $this->Put_Command("2");
  131.  
  132.     if ($this->type==1) {
  133.       $this->Put_Command("1");
  134.     }
  135.  
  136.     $this->Put_Command("");
  137.  
  138.     $this->Put_Command($port);
  139.     $this->Put_Command($action);
  140.  
  141.     $this->Put_Command("YES\r\n");
  142.  
  143.     $this->Put_Command(ESC,1);
  144.     $this->Put_Command(ESC,1);
  145.     $this->Put_Command(ESC,1);
  146.     $this->Put_Command(ESC,1);
  147.  
  148.     if ($this->type==1) {
  149.       $this->Put_Command(ESC,1);
  150.     }
  151.  
  152.     $this->Put_Command("4");
  153.  
  154.     $data=$this->Get_Data();
  155.     if ($data) {
  156.       return true;
  157.     }
  158.     else {
  159.       return false;
  160.     }
  161.   }
  162.   else {
  163.     return false;
  164.   }
  165. }
  166.  
  167. // Получение времени аптайма свича
  168. public function Get_Uptime() {
  169.   if ($this->telnet_login()) {
  170.     $this->Put_Command("4");
  171.     $data=$this->Get_Data();
  172.  
  173.     $pocket=Array();
  174.     eregi("Up Time   : ([0-9]+) Days? ([0-9]+) Hours?".
  175.       " ([0-9]+) Minutes?",$data,$pocket);
  176.     if (count($pocket)==4) {
  177.       $tmp=Array();
  178.       $tmp['days']=$pocket[1];
  179.       $tmp['hours']=$pocket[2];
  180.       $tmp['minutes']=$pocket[3];
  181.       return $tmp;
  182.     }
  183.     else {
  184.       $this->error=777;
  185.       $this->errormsg="Invalid data recived";
  186.       return false;
  187.     }
  188.   }
  189.   else {
  190.     return false;
  191.   }
  192. }
  193.  
  194. // Подключение по telnet
  195. private function telnet_login() {
  196.   if ($this->host=="") {
  197.     $this->error=777;
  198.     $this->errormsg="Invalid host";
  199.     return false;
  200.   }
  201.   if (intval($this->port)<=1) {
  202.     $this->error=777;
  203.     $this->errormsg="Invalid port";
  204.     return false;
  205.   }
  206.   if (intval($this->timeout)<=1) {
  207.     $this->error=777;
  208.     $this->errormsg="Invalid timeout";
  209.     return false;
  210.   }
  211.   $this->connection=@fsockopen($this->host$this->port,
  212.          $this->error$this->errormsg$this->timeout);
  213.   if (!$this->connection) {
  214.     return false;
  215.   }
  216.   fwrite($this->connection$this->login."\r\n");
  217.   fwrite($this->connection$this->password."\r\n");
  218.   return true;
  219. }
  220.  
  221. // Получение данных по telnet
  222. private function Get_Data() {
  223.   $data="";
  224.   while (!feof($this->connection)) {
  225.     $tmp=trim(fgets($this->connection,1000));
  226.     $data.=$tmp."\n";
  227.   }
  228.   fclose($this->connection);
  229.   if (trim($data)=="") {
  230.     $this->error=777;
  231.     $this->errormsg="Empty data recived. Please try again later";
  232.     return false;
  233.   }
  234.   else {
  235.     return $data;
  236.   }
  237. }
  238.  
  239. // Отправка данных по telnet
  240. private function Put_Command($cmd,$no_ret=0) {
  241.   fwrite($this->connection$cmd.($no_ret==0?"\r\n":""));
  242. }
  243.  
  244. // Вспомогательная функция получения типа свича, т.к. у разных
  245. // моделей отличается меню управления
  246. private function Get_Type() {
  247.   if ($this->telnet_login()) {
  248.     $this->Put_Command("1");
  249.     $this->Put_Command("2");
  250.  
  251.     $this->Put_Command("");
  252.  
  253.     $this->Put_Command(ESC,1);
  254.     $this->Put_Command(ESC,1);
  255.  
  256.     $this->Put_Command("4");
  257.  
  258.     $data=$this->Get_Data();
  259.     if ($data=="") {
  260.       return false;
  261.     }
  262.     if (strpos($data,"Outlet Control/Configuration")) {
  263.       $this->type=1;
  264.     }
  265.     else {
  266.       $this->type=0;
  267.     }
  268.     return true;
  269.   }
  270.   else {
  271.     return false;
  272.   }
  273. }
  274.  
  275. // ---------------------------
  276. }
  277. ?>
Доступные функции: Port_List - получение списка и состояния всех портов свича, Get_State - получение состояния отдельного порта свича, Turn_Port - изменение состояния (включение-выключение) порта, Get_Uptime - получение времени аптайма свича.

Пример использования скрипта. В нем удаленный сервер будет перезагружен путем отключения питания на свиче и включения его обратно через 5 секунд.
  1. <?
  2. $host='1.2.3.4'// IP-адрес свича
  3. $login='login';  // Логин для подключения по telnet
  4. $password='password';  // Пароль для подключения по telnet
  5. $port=5;         // Номер порта, который надо перезагрузить
  6.  
  7. // Инициализация класса
  8. $telnet = new telnet;
  9. $telnet->host=$host;
  10. $telnet->login=$login;
  11. $telnet->password=$password;
  12.  
  13. // Получить список и состояние портов свича
  14. if ($tmp=$telnet->Port_List()) {
  15.     // Если запрошенный порт есть, то перезагрузить сервер по питанию
  16.     if ($tmp[$port]['state']!="") {
  17.         // Выключить питание
  18.         if ($telnet->Turn_Port($portIMMEDIATE_OFF)) {
  19.             // Пауза 5 секунд
  20.             sleep(5);
  21.             // Включить питание
  22.             if (!$telnet->Turn_Port($portIMMEDIATE_ON)) {
  23.                 $error=$telnet->errormsg;
  24.             }
  25.         }
  26.         else {
  27.             $error=$telnet->errormsg;
  28.         }
  29.     }
  30.     else {
  31.         // Возможно, такого порта нет
  32.         $error=$telnet->errormsg;
  33.     }
  34. }
  35. else {
  36.     // Невозможно проверить состояние портов
  37.     $error=$telnet->errormsg;
  38. }
  39. ?>
Как видите, ничего сложного. На основе этого класса вы можете написать другой, для управления какими-нибудь другим устройствами, которые поддерживают протокол telnet.

Поделиться ссылкой ВКонтакте Поделиться ссылкой на Facebook Поделиться ссылкой на LiveJournal Поделиться ссылкой в Мой Круг Добавить в Мой мир Добавить на ЛиРу (Liveinternet) Добавить в закладки Memori Добавить в закладки Google
Просмотров: 4030 | Комментариев: 0

Метки: PHP, серверы
Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
Комментариeв нет

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2018
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.004 сек.) / Память: 4.75 Mb
Наверх