mirror of
https://github.com/aljazceru/plugins.git
synced 2025-12-19 14:14:20 +01:00
backup: Implement network backup
This commit is contained in:
committed by
Christian Decker
parent
4f4e30bb49
commit
804a9bb290
80
backup/socketbackend.py
Normal file
80
backup/socketbackend.py
Normal file
@@ -0,0 +1,80 @@
|
||||
import json
|
||||
import logging, socket, struct
|
||||
from typing import Tuple, Iterator
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from backend import Backend, Change
|
||||
from protocol import PacketType, recvall, PKT_CHANGE_TYPES, change_from_packet, packet_from_change, send_packet, recv_packet
|
||||
|
||||
class SocketBackend(Backend):
|
||||
def __init__(self, destination: str, create: bool):
|
||||
self.version = None
|
||||
self.prev_version = None
|
||||
self.destination = destination
|
||||
self.url = urlparse(self.destination)
|
||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
(host, port) = self.url.path.split(':')
|
||||
logging.info('Initialized socket backend')
|
||||
self.sock.connect((host, int(port)))
|
||||
logging.info('Connected to {}'.format(destination))
|
||||
|
||||
def _send_packet(self, typ: int, payload: bytes) -> None:
|
||||
send_packet(self.sock, typ, payload)
|
||||
|
||||
def _recv_packet(self) -> Tuple[int, bytes]:
|
||||
return recv_packet(self.sock)
|
||||
|
||||
def initialize(self) -> bool:
|
||||
'''
|
||||
Initialize socket backend by request current metadata from server.
|
||||
'''
|
||||
logging.info('Initializing backend')
|
||||
self._send_packet(PacketType.REQ_METADATA, b'')
|
||||
(typ, payload) = self._recv_packet()
|
||||
assert(typ == PacketType.METADATA)
|
||||
self.protocol, self.version, self.prev_version, self.version_count = struct.unpack("!IIIQ", payload)
|
||||
logging.info('Initialized SocketBackend: protocol={}, version={}, prev_version={}, version_count={}'.format(
|
||||
self.protocol, self.version, self.prev_version, self.version_count
|
||||
))
|
||||
return True
|
||||
|
||||
def add_change(self, entry: Change) -> bool:
|
||||
typ, payload = packet_from_change(entry)
|
||||
self._send_packet(typ, payload)
|
||||
# Wait for change to be acknowledged before continuing.
|
||||
(typ, _) = self._recv_packet()
|
||||
assert(typ == PacketType.ACK)
|
||||
return True
|
||||
|
||||
def rewind(self) -> bool:
|
||||
'''Rewind to previous version.'''
|
||||
version = struct.pack("!I", self.prev_version)
|
||||
self._send_packet(PacketType.REWIND, version)
|
||||
# Wait for change to be acknowledged before continuing.
|
||||
(typ, _) = self._recv_packet()
|
||||
assert(typ == PacketType.ACK)
|
||||
return True
|
||||
|
||||
def stream_changes(self) -> Iterator[Change]:
|
||||
self._send_packet(PacketType.RESTORE, b'')
|
||||
version = -1
|
||||
while True:
|
||||
(typ, payload) = self._recv_packet()
|
||||
if typ in PKT_CHANGE_TYPES:
|
||||
change = change_from_packet(typ, payload)
|
||||
version = change.version
|
||||
yield change
|
||||
elif typ == PacketType.DONE:
|
||||
break
|
||||
else:
|
||||
raise ValueError("Unknown entry type {}".format(typ))
|
||||
|
||||
if version != self.version:
|
||||
raise ValueError("Versions do not match up: restored version {}, backend version {}".format(version, self.version))
|
||||
assert(version == self.version)
|
||||
|
||||
def compact(self):
|
||||
self._send_packet(PacketType.COMPACT, b'')
|
||||
(typ, payload) = self._recv_packet()
|
||||
assert(typ == PacketType.COMPACT_RES)
|
||||
return json.loads(payload.decode())
|
||||
Reference in New Issue
Block a user