пітона подпроцессов тупіковая ў дэманізаваць сэрвэры

Я спрабую наладзіць аддалены сервер рэзервовага капіявання для Дара, ўздоўж гэтых ліній . Я б вельмі хацеў, каб зрабіць усё трубы з пітонам, калі гэта магчыма, але я папрасіў асобнае пытанне пра гэта.

Выкарыстанне Netcat ў subprocess.Popen (CMD, абалонкі = True) , мне ўдалося зрабіць дыферэнцыяльную рэзервовую копію, як у прыкладах на сайце ДАР. Адзіныя дзве праблемы:

  1. Не ведаю, як прызначыць нумары партоў дынамічна гэты шлях
  2. Калі я выканаць сервер у фонавым рэжыме, ён трывае няўдачу. Чаму?

Update: This doesn't seem to be related to netcat; it hangs even without netcat in the mix.

Вось мой код:

from socket import socket, AF_INET, SOCK_STREAM
import os, sys
import SocketServer
import subprocess

class DarHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        print('entering handler')
        data = self.request.recv(1024).strip()
        print('got: ' + data)
        if data == 'xform':
            cmd1 = 'nc -dl 41201 | dar_slave archives/remotehost | nc -l 41202'
            print(cmd1)
            cmd2 = 'nc -dl 41200 | dar_xform -s 10k - archives/diffbackup'
            print(cmd2)
            proc1 = subprocess.Popen(cmd1, shell=True)
            proc2 = subprocess.Popen(cmd2, shell=True)
            print('sending port number')
            self.request.send('41200')
            print('waiting')
            result = str(proc1.wait())
            print('nc-dar_slave-nc returned ' + result)
            result = str(proc2.wait())
            print('nc-dar_xform returned ' + result)
        else:
            result = 'bad request'
        self.request.send(result)
        print('send result, exiting handler')

myaddress = ('localhost', 18010)
def server():
    server = SocketServer.TCPServer(myaddress, DarHandler)
    print('listening')
    server.serve_forever()

def client():
    sock = socket(AF_INET, SOCK_STREAM)
    print('connecting')
    sock.connect(('localhost', 18010))
    print('connected, sending request')
    sock.send('xform')
    print('waiting for response')
    port = sock.recv(1024)
    print('got: ' + port)
    try:
        os.unlink('toslave')
    except:
        pass
    os.mkfifo('toslave')
    cmd1 = 'nc -w3 localhost 41201 < toslave'
    cmd2 = 'nc -w3 localhost 41202 | dar -B config/test.dcf -A - -o toslave -c - | nc -w3 localhost ' + port
    print(cmd2)
    proc1 = subprocess.Popen(cmd1, shell=True)
    proc2 = subprocess.Popen(cmd2, shell=True)
    print('waiting')
    result2 = proc2.wait()
    result1 = proc1.wait()
    print('nc

Вось што адбываецца на сэрвэры:

$ python clientserver.py serve &
[1] 4651
$ listening
entering handler
got: xform
nc -dl 41201 | dar_slave archives/remotehost | nc -l 41202
nc -dl 41200 | dar_xform -s 10k - archives/diffbackup
sending port number
waiting

[1]+  Stopped                 python clientserver.py serve

Вось што адбываецца на баку кліента:

$ python clientserver.py client
connecting
connected, sending request
waiting for response
got: 41200
nc -w3 localhost 41202 | dar -B config/test.dcf -A - -o toslave -c - | nc -w3 localhost 41200
waiting
FATAL error, aborting operation
Corrupted data read on pipe
nc

Акрамя таго, кліент вісіць, і я павінен забіць яго з дапамогай перапынення клавіятуры.

1
Чаму павінны быць ўцягнутыя пітон? <Код> Дар - | пс і пс -l | Дар здаецца нашмат прасцей.
дададзена аўтар Gringo Suave, крыніца
Што здарылася з хрон? Я не бачу сэнсу выкарыстоўваць як пс і пітона для сокетаў, чаму не адзін ці іншы?
дададзена аўтар Gringo Suave, крыніца
@gringo, што <я> здаецца прасцей, але я не хачу, каб выклікаць ўсе мае рэзервовыя копіі ўручную. Крон не дадумаліся. Кадаваньне сервера рэзервовага капіявання ў Баше будзе даволі балючым. Дык што ж вы прапануеце?
дададзена аўтар Aryeh Leib Taurog, крыніца

2 адказы

Я б скараціць свае страты і пачаць усё спачатку. Гэтая спроба рашэння з'яўляецца вельмі складанай і заблытанай. Ёсць шмат гатовых рашэнняў у вобласці:

Fwbackups гучыць добра, калі вы хочаце ўзяць лёгкі маршрут, Rsync + SSH для жорсткага ядра.

1
дададзена
+1 для прагматычнага падыходу. Я, на жаль, не магу спаць, калі тэхнічная праблема, як гэта бянтэжыць мяне. Я лічыў Bacula і Аманду, але я меркаваў, што гэта будзе лічыць мяне амаль гэтак жа доўга, каб зразумець іх, як бы каціцца самастойна. Праблема з Rsync, які таксама быў павабным, гэта не рабіць правільныя рэчы амаль гэтак жа часта, як Дар, а таксама з Дарма я магу зашыфраваць сваю рэзервовую копію і ўставіць іх лёгка на Амазонка s3.
дададзена аўтар Aryeh Leib Taurog, крыніца
  1. Use Popen.communicate() instead of Popen.wait().

    The python documentation for wait() states:

    Warning: This will deadlock if the child process generates enough output to a stdout or stderr pipe such that it blocks waiting for the OS pipe buffer to accept more data. Use communicate() to avoid that.

  2. Dar and its related executables should get a -Q if they aren't running interactively.

  3. When syncronizing multiple processes, make sure to call communicate() on the 'weakest link' first: dar_slave before dar_xform and dar before cat. This was done correctly in the question, but it's worth noting.

  4. Clean up shared resources. The client process is holding open a socket from which dar_xform is still reading. Attempting to send/recv data on the initial socket after dar and friends are finished without closing that socket will therefore cause a deadlock.

Here is a working example which doesn't use shell=True or netcat. An advantage of this is I can have the secondary ports assigned dynamically and therefore could conceivably serve multiple backup clients simultaneously.

1
дададзена