Запуск локальных ssh/telnet/vnc клиентов по ссылке из карты Zabbix

от автора

Множество стоек, каждая плотно упакована серверами, маршрутизаторами, коммутаторами и прочими kvm’ами.
Нужен какой-нибудь удобный способ рулить всем этим хозайством, быстро подключаться к нужному оборудованию и
производить его настройку. Прямо чтобы пара кликов мышью и оп — перед тобой консоль нужного коммутатора.

Для мониторинга наших подопечных мы используем Zabbix.
Так почему бы не приспособить сей дивный инструмент и для этой задачи.
Ведь было бы очень удобно ткнуть в карте Zabbix на нужную стойку, перейти на её подкарту и, выбрав железку,
запустить локальный ssh/telnet/vnc клиент на своем компьютере.

Озадачившись идеей, я начал мучать поисковые машины в надежде отыскать варианты реализации.
Был найден данный тред на форуме Zabbix, но мне хотелось запускать именно локальные программы на моей машине кликом по ссылке в карте.
Еще некоторое время поплутав по закоулкам всемирной паутины и помучав знакомых программистов глупыми вопросами я вспомнил о… Python.
Да, Python, не раз пришедший на помощь в трудную минуту.
Питаю очень нежные чувства к этому языку за его простоту и приятный теплый синтаксис.

И так, вектор атаки изменился и поисковики замерли в ожидании нового вброса мыслеобразов…
Спустя некоторое время я уже четко представлял как буду решать задачу — напишу клиент-серверное приложение!
На моем компьютере будет ждать команд серверная часть, а на сервере мониторинга, при клике по ссылке, будет запускаться клиент и передавать нужную команду.

Результатом изысканий стало кроссплатформенное приложение, работает как на Linux, так и на Windows.
Эпопея проб и ошибок на пути к заветной цели ждет вас под хабракатом.

Пролог

Начало пути.

В первую очередь отмечу, что синтаксис написанного справедлив для Python версии 2.7.
А версия Zabbix насчитала три двойки — Zabbix 2.2.2
Отвечать за коммуникацию клиента и сервера поручено Python библиотеке socket.
Помимо документации мне были полезны следующие источники:
1. www.binarytides.com/python-socket-programming-tutorial/
2. www.binarytides.com/code-chat-application-server-client-sockets-python/
3. java-developer.livejournal.com/6920.html

Наигравшись с примерами, подготовим основу будущего приложения.
Клиент:

#!/usr/bin/env python # -*- coding: utf-8 -*- import socket  sock = socket.socket() sock.connect(('localhost', 9090)) sock.send('/usr/bin/konsole -e  mc')  sock.close() 

И сервер:

#!/usr/bin/env python # -*- coding: utf-8 -*- import socket import subprocess  sock = socket.socket() sock.bind(('localhost', 9090)) sock.listen(1) while True:         conn, addr = sock.accept()         data = conn.recv(1024)         if not data:                 break         subprocess.call(data, shell=True) conn.close() 

Запущеный сервер «слушает» порт 9090 на интерфейсе обратной петли, выполнив клиента мы передаем команду для запуска новой консоли, а параметр -e mc указывает, что в консоли нужно запустить файловый менеджер midnight commander.

Краткая демонстрация

*Основной ОС для меня является Linux, а в качестве DE выступает KDE, но пусть вас это не смущает, итоговый вариант скриптов не будет привязан к ОС.

Основа положена, двигаемся далее!

Глава 1

В этой главе клиент научится понимать аргументы командной строки и мы отправим серверу первую команду с карты Zabbix.

Код серверной части пока не претерпел никаких изменений, а вот в код клиента добавился импорт библиотеки sys.

#!/usr/bin/env python # -*- coding: utf-8 -*- import socket import sys  sock = socket.socket() sock.connect(('localhost', 9090)) sock.send('/usr/bin/konsole -e ssh root@'+ sys.argv[1])  sock.close() 

Сервер должен будет запустить консоль и инициировать подключение по ssh к IP из первого аргумента переданного клиенту.
Логином выступает root.

А теперь пришло время поместить скрипт клиента на сервер мониторинга.
В моем случае он уютно устроился здесь — /usr/local/bin/client.py
В веб интерфейсе Zabbix перейдем в Administration -> Scripts.

Создадим новый скрипт(Create new).

Через Zabbix макрос {HOST.IP} клиенту передается IP для подключения.
Перейдя на карту увидим, что у хостов появилось меню с именем вновь созданного скрипта.

Многие справедливо заметят — «Скрипт клиента до сих пор соединяется с localhost, но он же уже на сервере?!»
Все верно! Но для защиты взаимодействия сервера и клиента используется ssh туннель.
Все соединения устанавливаемые на localhost:9090 Zabbix сервера на самом деле устанавливаются со скриптом сервера на нашей локальной машине.
Для достижения такого «зеркалирования» на локальном компьютере нужно выполнить команду:

ssh -f -N -R 9090:127.0.0.1:9090  zabbix 

Подробнее о ssh, и тоннелях в часности, можно прочесть в замечательных статьях на хабре — 1, 2

Видеоиллюстрация к первой главе

Глава 2

В этой главе скрипты еще немного поумнеют и научатся отличать Windows от Linux.

Идея состоит в том, чтобы клиент при соединении с сервером первым делом узнавал на какой ОС тот запущен.
И, получив это информацию, отдавал правильную команду.
Код обоих скриптов изменился.

Клиент:

#!/usr/bin/env python # -*- coding: utf-8 -*- import socket import sys  sock = socket.socket() sock.connect(('localhost', 9090))  os = sock.recv(64) if  os == "Windows":     sock.send('C:\\apps\\putty.exe -ssh root@' + sys.argv[1]) elif os == "Linux":     sock.send('/usr/bin/konsole -e ssh root@'+ sys.argv[1])  sock.close() 

Сервер:

#!/usr/bin/env python # -*- coding: utf-8 -*- import socket import subprocess  OS = "Linux"  sock = socket.socket() sock.bind(('127.0.0.1', 9090)) sock.listen(1) while True:     conn, addr = sock.accept()     conn.sendall(OS)     data = conn.recv(1024)     print data     if not data:         break     subprocess.call(data, shell=True) conn.close() 

* для Windows переменная OS должна быть изменена соответствующе
Как видно, при коннекте к серверу клиент перво-наперво получает строку с названием ОС, и только потом посылает команду.
Обратите внимание, что бекслэши в путях для ОС Windows нужно экранировать, т.е. ставить двойной \\

Видео ко второй главе


На видео:
«зеркалируем» порт в Windows запустив putty
запускаем сервер
кликаем ссылку для соединения с хостом
отключаем все в Windows и проделываем то же самое в Linux

Глава 3

В этой главе мы немного подправим код фронтенда Zabbix.
Научим его передавать скрипту-клиенту вторым аргументом логин зарегистрированного на веб интерфейсе администратора/оператора.

Вроде бы все здорово и уже сейчас можно пользоваться наработками для соединения с серверами, маршрутизаторами, коммутаторами.
Но что если администраторов, пользующихся Zabbix, больше одного?
Нам нужно каким-то образом дать понять скрипту кто его запустил, попросту говоря кто «ткнул» ссылку в веб интерфейсе.

Решать будем поэтапно.

  • Закрепим за каждым администратором определенный порт.
  • Научим скрипт-клиент выбирать порт в зависимости от второго аргумента — логина.
  • Изменим код Zabbix для передачи второго аргумента при клике по ссылке из веб интерфейса.

Порты:
Admin — 9090
Admin1 — 9091
и т.д.

Код скрипта-клиента с нужными изменениями:

#!/usr/bin/env python # -*- coding: utf-8 -*-  import socket import sys  if sys.argv[2] == 'Admin':         PORT=9090 elif sys.argv[2] == 'Admin1':         PORT=9091  sock = socket.socket() sock.connect(('localhost', PORT)) os = sock.recv(64)  if  os == "Windows":         sock.send('C:\\apps\\putty.exe -ssh root@' + sys.argv[1]) elif os == "Linux":         sock.send('/usr/bin/konsole -e ssh root@'+ sys.argv[1])  sock.close() 

А вот решить последнюю задачу, в рамках данной главы, оказалось не так просто.
Пришлось существенно увеличить лимит кофе на эту смену и даже умыкнуть кота у жены из под бока.
Кофе был выпит, кот, промурлыкав положенное, давно свалил, а я ни на йоту не приблизился к решению…
Но ощущение близости отгадки не давало мне покоя, оно крепко вцепилось в мой мозг своими клешнями, и я знал, что яблоко вот-вот должно свалиться мне на голову.
И оно свалилось!(не в прямом смысле конечно)
Работающий вариант, как это часто бывает, был практически на поверхности.
Для выполнения всех скриптов, вызванных из веб интерфейса, используется scripts_exec.php, расположеный в корневом каталоге установленного Zabbix.
Описание скриптов хранится в MySQL базе.

Значит исправив должным образом scripts_exec.php мы сможем передавать логин как второй аргумент для нашего скрипта-клиента при клике по ссылке.

Код исправленного scripts_exec.php:

<?php /* ** Zabbix ** Copyright (C) 2001-2014 Zabbix SIA ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. **/   require_once dirname(__FILE__).'/include/config.inc.php';  $page['title'] = _('Scripts'); $page['file'] = 'scripts_exec.php';  define('ZBX_PAGE_NO_MENU', 1);  require_once dirname(__FILE__).'/include/page_header.php';  $NewCommand4Run = '/usr/local/bin/client_ssh.py {HOST.IP} '. CWebUser::$data['alias'];   // VAR  TYPE    OPTIONAL        FLAGS   VALIDATION      EXCEPTION $fields = array(         'hostid' =>             array(T_ZBX_INT, O_OPT, P_ACT, DB_ID, null),         'scriptid' =>   array(T_ZBX_INT, O_OPT, null, DB_ID, null) ); check_fields($fields);  ob_flush(); flush();  $scriptId = getRequest('scriptid'); $hostId = getRequest('hostid');  $data = array(         'message' => '',         'info' => DBfetch(DBselect('SELECT s.name FROM scripts s WHERE s.scriptid='.zbx_dbstr($scriptId))) );  if ($scriptId == 5) {                 DBexecute('update zabbix.scripts set command='."'".$NewCommand4Run."'".' where scriptid=5;');         }  $result = API::Script()->execute(array(         'hostid' => $hostId,         'scriptid' => $scriptId ));  $isErrorExist = false;  if (!$result) {         $isErrorExist = true; } elseif ($result['response'] == 'failed') {         error($result['value']);          $isErrorExist = true; } else {         $data['message'] = $result['value']; }  if ($isErrorExist) {         show_error_message(                 _('Cannot connect to the trapper port of zabbix server daemon, but it should be available to run the script.')         ); }  // render view $scriptView = new CView('general.script.execute', $data); $scriptView->render(); $scriptView->show();  require_once dirname(__FILE__).'/include/page_footer.php'; 

Переменная

$NewCommand4Run = '/usr/local/bin/client.py {HOST.IP} '. CWebUser::$data['alias']; 

после нажатия ссылки, будет содержать путь к скрипту + макрос {HOST.IP} + логин админа.
А условие

if ($scriptId == 5) {                 DBexecute('update zabbix.scripts set command='."'".$NewCommand4Run."'".' where scriptid=5;');         } 

заменит в таблице MySQL команду на нужную нам.
Например вот так:

И теперь скрипт-клиент сможет передать команду нужному скрипту-серверу.

А вот как это выглядит.

Смотреть лучше без звука, ноут у меня разбушевался уж очень)

Глава 4

В этой главе мы научим Zabbix запускать vnc и telnet клиентов.

Поговорим о скриптах-клиентах для прочих, перечисленных в заголовке, протоколов.
Листинг директории /usr/local/bin на Zabbix сервере:

-rwxr-xr-x 1 root staff 429 Mar 17 03:06 client_ssh.py -rwxr-xr-x 1 root staff 418 Mar 17 04:58 client_telnet.py -rwxr-xr-x 1 root staff 412 Mar 17 05:00 client_vnc.py 

Листинг каждого скрипта-клиента

client_ssh.py

#!/usr/bin/env python # -*- coding: utf-8 -*-  import socket import sys  if sys.argv[2] == 'Admin':         PORT=9090 elif sys.argv[2] == 'Admin1':         PORT=9091  sock = socket.socket() sock.connect(('localhost', PORT)) os = sock.recv(64)  if  os == "Windows":         sock.send('C:\\apps\\putty.exe -ssh root@' + sys.argv[1]) elif os == "Linux":         sock.send('/usr/bin/konsole -e ssh root@'+ sys.argv[1])  sock.close() 

client_vnc.py

#!/usr/bin/env python # -*- coding: utf-8 -*-  import socket import sys  if sys.argv[2] == 'Admin':         PORT=9090 elif sys.argv[2] == 'Admin1':         PORT=9091  sock = socket.socket() sock.connect(('localhost', PORT)) os = sock.recv(64)  if  os == "Windows":         sock.send('c:\\apps\\vnc.exe ' + sys.argv[1]) elif os == "Linux":         sock.send('/usr/bin/X11/gvncviewer '+ sys.argv[1])  sock.close() 

client_telnet.py

#!/usr/bin/env python # -*- coding: utf-8 -*- import socket import sys  if sys.argv[2] == 'Admin':         PORT=9090 elif sys.argv[2] == 'Admin1':         PORT=9091  sock = socket.socket() sock.connect(('localhost', PORT))  os = sock.recv(64) if  os == "Windows":     sock.send('C:\\apps\\putty.exe -telnet ' + sys.argv[1]) elif os == "Linux":     sock.send('/usr/bin/konsole -e telnet '+ sys.argv[1])   sock.close() 

Подробно останавливаться на коде не будем, в предыдущих главах это уже было сделано.
Скажу лишь, что для Windows я скачал portable версию vnc клиента и положил все в c:\apps\

Для telnet и vnc из скриптов нужно повторить шаги из главы 1, в веб интерфейсе Zabbix, а для ssh достаточно скорректировать название.
Файл scripts_exeс.php так же нуждается в изменении.

Вновь измененный код scripts_exeс.php

<?php /* ** Zabbix ** Copyright (C) 2001-2014 Zabbix SIA ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. **/   require_once dirname(__FILE__).'/include/config.inc.php';  $page['title'] = _('Scripts'); $page['file'] = 'scripts_exec.php';  define('ZBX_PAGE_NO_MENU', 1);  require_once dirname(__FILE__).'/include/page_header.php';  $NewCommand4RunSSH = '/usr/local/bin/client_ssh.py {HOST.IP} '. CWebUser::$data['alias']; $NewCommand4RunVNC = '/usr/local/bin/client_vnc.py {HOST.IP} '. CWebUser::$data['alias']; $NewCommand4RunTELNET = '/usr/local/bin/client_telnet.py {HOST.IP} '. CWebUser::$data['alias'];   // VAR  TYPE    OPTIONAL        FLAGS   VALIDATION      EXCEPTION $fields = array(         'hostid' =>             array(T_ZBX_INT, O_OPT, P_ACT, DB_ID, null),         'scriptid' =>   array(T_ZBX_INT, O_OPT, null, DB_ID, null) ); check_fields($fields);  ob_flush(); flush();  $scriptId = getRequest('scriptid'); $hostId = getRequest('hostid');  $data = array(         'message' => '',         'info' => DBfetch(DBselect('SELECT s.name FROM scripts s WHERE s.scriptid='.zbx_dbstr($scriptId))) );  if ($scriptId == 5) {                 DBexecute('update zabbix.scripts set command='."'".$NewCommand4RunSSH."'".' where scriptid=5;');         } if ($scriptId == 6) {                 DBexecute('update zabbix.scripts set command='."'".$NewCommand4RunVNC."'".' where scriptid=6;');         } if ($scriptId == 7) {                 DBexecute('update zabbix.scripts set command='."'".$NewCommand4RunTELNET."'".' where scriptid=7;');         }   $result = API::Script()->execute(array(         'hostid' => $hostId,         'scriptid' => $scriptId ));  $isErrorExist = false;  if (!$result) {         $isErrorExist = true; } elseif ($result['response'] == 'failed') {         error($result['value']);          $isErrorExist = true; } else {         $data['message'] = $result['value']; }  if ($isErrorExist) {         show_error_message(                 _('Cannot connect to the trapper port of zabbix server daemon, but it should be available to run the script.')         ); }  // render view $scriptView = new CView('general.script.execute', $data); $scriptView->render(); $scriptView->show();  require_once dirname(__FILE__).'/include/page_footer.php'; 

Уже традиционное видео.

Здесь тоже лучше без звука.

Заключение.

Наведем порядок и придадим некоторый лоск нашей разработке.

Строго говоря, здесь кросплатформеность и заканчивается.

Для ОС Linux, из множества примеров, был собран демон на Python.
Он выполняет дабл форк и преспокойно ждет команд от скриптов-клиентов.
В ОС Windows все оказалось сложнее, но, обо всем по порядку.

Листинг директории с необходимыми скриптами для ОС Linux:

fessae@workbook:~/sh/python > pwd /home/fessae/sh/python fessae@workbook:~/sh/python > ll total 12 -rwxr-xr-x 1 fessae fessae 2880 Feb 28 02:56 daemon.py* -rwxr-xr-x 1 fessae fessae  710 Mar 17 03:36 server.py* -rwxr-xr-x 1 fessae fessae 1122 Mar 13 04:59 ZabbixTeams.py* 

Код каждого из скриптов серверной части Linux.

daemon.py

#!/usr/bin/env python  import sys, os, time, atexit from signal import SIGTERM  class Daemon:         """         A generic daemon class.          Usage: subclass the Daemon class and override the run() method         """         def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):                 self.stdin = stdin                 self.stdout = stdout                 self.stderr = stderr                 self.pidfile = pidfile          def daemonize(self):                 """                 do the UNIX double-fork magic, see Stevens' "Advanced                 Programming in the UNIX Environment" for details (ISBN 0201563177)                 http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16                 """                 try:                         pid = os.fork()                         if pid > 0:                                 # exit first parent                                 sys.exit(0)                 except OSError, e:                         sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))                         sys.exit(1)                  # decouple from parent environment                 os.chdir("/")                 os.setsid()                 os.umask(0)                  # do second fork                 try:                         pid = os.fork()                         if pid > 0:                                 # exit from second parent                                 sys.exit(0)                 except OSError, e:                         sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))                         sys.exit(1)                  # redirect standard file descriptors                 sys.stdout.flush()                 sys.stderr.flush()                 si = file(self.stdin, 'r')                 so = file(self.stdout, 'a+')                 se = file(self.stderr, 'a+', 0)                 os.dup2(si.fileno(), sys.stdin.fileno())                 os.dup2(so.fileno(), sys.stdout.fileno())                 os.dup2(se.fileno(), sys.stderr.fileno())                  # write pidfile                 atexit.register(self.delpid)                 pid = str(os.getpid())                 file(self.pidfile,'w+').write("%s\n" % pid)          def delpid(self):                 os.remove(self.pidfile)          def start(self):                 """                 Start the daemon                 """                 # Check for a pidfile to see if the daemon already runs                 try:                         pf = file(self.pidfile,'r')                         pid = int(pf.read().strip())                         pf.close()                 except IOError:                         pid = None                  if pid:                         message = "pidfile %s already exist. Daemon already running?\n"                         sys.stderr.write(message % self.pidfile)                         sys.exit(1)                  # Start the daemon                 self.daemonize()                 self.run()          def stop(self):                 """                 Stop the daemon                 """                 # Get the pid from the pidfile                 try:                         pf = file(self.pidfile,'r')                         pid = int(pf.read().strip())                         pf.close()                 except IOError:                         pid = None                  if not pid:                         message = "pidfile %s does not exist. Daemon not running?\n"                         sys.stderr.write(message % self.pidfile)                         return # not an error in a restart                  # Try killing the daemon process                 try:                         while 1:                                 os.kill(pid, SIGTERM)                                 time.sleep(0.1)                 except OSError, err:                         err = str(err)                         if err.find("No such process") > 0:                                 if os.path.exists(self.pidfile):                                         os.remove(self.pidfile)                         else:                                 print str(err)                                 sys.exit(1)          def restart(self):                 """                 Restart the daemon                 """                 self.stop()                 self.start()  def run(self):                 """                 You should override this method when you subclass Daemon. It will be called after the process has been                 daemonized by start() or restart().         """ 

server.py

#!/usr/bin/env python # -*- coding: utf-8 -*-  import socket import subprocess import sys  OS = "Linux"  def main():         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)         print 'Socket created'         try:             sock.bind(('127.0.0.1', 9090))         except socket.error , msg:             print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]             sys.exit()         print 'Socket bind complete'         sock.listen(1)         print 'Socket now listening'          while True:                 conn, addr = sock.accept()                 conn.sendall(OS)                 data = conn.recv(1024)                 print 'Connected with ' + addr[0] + ':' + str(addr[1])                 print data                 if not data:                         break                 subprocess.call(data, shell=True)         conn.close()  if __name__ == "__main__":     main() 

ZabbixTeams.py

#/usr/bin/env python #-*- coding: utf-8 -*-                                                                                                                             import sys sys.path.append("/home/fessae/sh/python") from daemon import Daemon import server  class ZabbixTeams(Daemon):             def run(self):                 while True:                     server.main()                               if __name__ == "__main__":             daemon = ZabbixTeams('/tmp/zabbixteams.pid',stdout='/tmp/zabbixteams.log',stderr='/tmp/zabbixteamserr.log')             if len(sys.argv) == 2:                     if 'start' == sys.argv[1]:                             daemon.start()                     elif 'stop' == sys.argv[1]:                             daemon.stop()                     elif 'restart' == sys.argv[1]:                             daemon.restart()                     else:                             print "Unknown command"                             sys.exit(2)                     sys.exit(0)             else:                     print "usage: %s start|stop|restart" % sys.argv[0]                     sys.exit(2) 

Для запуска демона набираем

python ZabbixTeams.py start 

Для остановки соответственно

python ZabbixTeams.py stop 

Обратите внимание на строку

sys.path.append("/home/fessae/sh/python") 

из ZabbixTeams.py, она подключает врзможность импортировать скрипты из этого каталога.
Исправьте её в соответствии вашему окружению.

Получившийся демон можно стартовать руками, прилипить иконку для его запуска, завернуть в австостарт DE или ОС.
Это уже кому как нравится.

ОС Windows.
Для написания/исправления кода я пользовался Python IDE PyCharm, поэтому и скрипты разместились по пути:

Правильнее, конечно, было бы написать сервис для этой ОС при помощи pywin32.
Но в скриптах использованы блокирующие сокеты и не отказавшись от них полноценный сервис у меня не получался.
Возможно позже я возьмусь за asyncore или вообще перепишу все на twisted, но пока мне не хотелось усложнять.

Поэтому запуск демона на Windows выглядит следующим образом.

  • на рабочем столе расположен файл server.bat содержащий:
     c:\Python27\python.exe C:\Users\FessAectan\PycharmProjects\ZabbixTeams\daemon.py 

  • daemon.py, в свою очередь, выглядит вот так:
    import sys import subprocess CREATE_NO_WINDOW = 0x8000000 subprocess.Popen(["c:\Python27\python.exe", "-O","C:\Users\FessAectan\PycharmProjects\ZabbixTeams\server.py"], shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, creationflags = CREATE_NO_WINDOW) 

  • и, конечно же, сам server.py
    #!/usr/bin/env python # -*- coding: utf-8 -*-  import socket import subprocess  OS = "Windows"  def main():     sock = socket.socket()     print "Socket created"     try:         sock.bind(('127.0.0.1', 9091))     except socket.error , msg:         print "Bind failed. Error Code : " + str(msg[0]) + " Message " + msg[1]         sys.exit()     print "Socket bind complete"     sock.listen(1)     print "Socket now listening"              while True:         conn, addr = sock.accept()         conn.sendall(OS)         data = conn.recv(1024)         print "Connected with " + addr[0] + ":" + str(addr[1])         if not data:             break         subprocess.Popen(data, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, creationflags = 0x00000008)     conn.close()   if __name__ == "__main__":         main()  

    Для Windows subprocess.call был заменен на subprocess.Popen.

Итоговое видео.
*со звуком все в порядке 😉

Благодарю за внимание.
by FessAectan

ссылка на оригинал статьи http://habrahabr.ru/company/serverclub/blog/217161/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *