From f29e76c928f79dd3b140fdfb5a267c98bf9c96a5 Mon Sep 17 00:00:00 2001 From: Joerg Steffens Date: Sun, 15 Nov 2015 13:14:41 +0100 Subject: do network reconnect --- bareos/bsock/bsock.py | 23 ++++++++++++++++---- bareos/bsock/bsockjson.py | 19 ++++++++++------- bareos/bsock/lowlevel.py | 53 +++++++++++++++++++++++++++++++++++++++-------- bareos/exceptions.py | 6 ++++++ 4 files changed, 81 insertions(+), 20 deletions(-) diff --git a/bareos/bsock/bsock.py b/bareos/bsock/bsock.py index 746c270..60a220b 100644 --- a/bareos/bsock/bsock.py +++ b/bareos/bsock/bsock.py @@ -2,8 +2,9 @@ Communicates with the bareos-director """ -from bareos.bsock.lowlevel import LowLevel -import sys +from bareos.bsock.lowlevel import LowLevel +from bareos.exceptions import * +import sys class BSock(LowLevel): '''use to send and receive the response from director''' @@ -24,9 +25,23 @@ class BSock(LowLevel): ''' call a bareos-director user agent command ''' - self.send(str(command)) - return self.recv_msg() + return self.__call(command, 0) + def __call(self, command, count): + ''' + Call a bareos-director user agent command. + If connection is lost, try to reconnect. + ''' + result = '' + try: + self.send(str(command)) + result = self.recv_msg() + except (SocketEmptyHeader, ConnectionLostError) as e: + self.logger.error("connection problem (%s): %s" % (type(e).__name__, str(e))) + if count == 0: + if self.reconnect(): + return self.__call(command, count+1) + return result def send_command(self, commamd): return self.call(command) diff --git a/bareos/bsock/bsockjson.py b/bareos/bsock/bsockjson.py index 9fd67c0..a14622c 100755 --- a/bareos/bsock/bsockjson.py +++ b/bareos/bsock/bsockjson.py @@ -20,11 +20,6 @@ class BSockJson(BSock): super(BSockJson, self).__init__( address, port, dirname, name, password) - # older version did not support compact mode, - # therfore first set api mode to json (which should always work in bareos >= 15.2.0) - # and then set api mode json compact (which should work with bareos >= 15.2.2) - self.call(".api json") - self.call(".api json compact=yes") def call(self, command): @@ -39,7 +34,6 @@ class BSockJson(BSock): return result - def call_fullresult(self, command): resultstring = super(BSockJson, self).call(command) data = None @@ -56,7 +50,6 @@ class BSockJson(BSock): 'data': resultstring }, } - return data @@ -71,3 +64,15 @@ class BSockJson(BSock): if command: pprint(self.call(command)) return True + + + def _set_state_director_prompt(self): + result = False + if super(BSockJson, self)._set_state_director_prompt(): + # older version did not support compact mode, + # therfore first set api mode to json (which should always work in bareos >= 15.2.0) + # and then set api mode json compact (which should work with bareos >= 15.2.2) + self.call(".api json") + self.call(".api json compact=yes") + result = True + return result diff --git a/bareos/bsock/lowlevel.py b/bareos/bsock/lowlevel.py index 4838715..dc63417 100644 --- a/bareos/bsock/lowlevel.py +++ b/bareos/bsock/lowlevel.py @@ -31,6 +31,7 @@ class LowLevel(object): self.port = None self.dirname = None self.socket = None + self.auth_credentials_valid = False def connect(self, address="localhost", port=9101, dirname=None): @@ -43,6 +44,10 @@ class LowLevel(object): self.dirname = dirname else: self.dirname = address + return self.__connect() + + + def __connect(self): try: self.socket = socket.create_connection((self.address, self.port)) except socket.gaierror as e: @@ -51,6 +56,7 @@ class LowLevel(object): "failed to connect to host " + str(self.address) + ", port " + str(self.port) + ": " + str(e)) else: self.logger.debug("connected to " + str(self.address) + ":" + str(self.port)) + return True def auth(self, password, name="*UserAgent*"): @@ -62,15 +68,22 @@ class LowLevel(object): ''' if not isinstance(password, Password): raise AuthenticationError("password must by of type bareos.Password() not %s" % (type(password))) - bashed_name = ProtocolMessages.hello(name) + self.password = password + self.name = name + return self.__auth() + + + def __auth(self): + bashed_name = ProtocolMessages.hello(self.name) # send the bash to the director self.send(bashed_name) - (ssl, result_compatible, result) = self._cram_md5_respond(password=password.md5(), tls_remote_need=0) + (ssl, result_compatible, result) = self._cram_md5_respond(password=self.password.md5(), tls_remote_need=0) if not result: raise AuthenticationError("failed (in response)") - if not self._cram_md5_challenge(clientname=name, password=password.md5(), tls_local_need=0, compatible=True): + if not self._cram_md5_challenge(clientname=self.name, password=self.password.md5(), tls_local_need=0, compatible=True): raise AuthenticationError("failed (in challenge)") + self.auth_credentials_valid = True return True @@ -80,10 +93,20 @@ class LowLevel(object): pass + def reconnect(self): + result = False + if self.auth_credentials_valid: + try: + if self.__connect() and self.__auth() and self._set_state_director_prompt(): + result = True + except socket.error: + self.logger.warning("failed to reconnect") + return result + + def send(self, msg=None): '''use socket to send request to director''' - if self.socket == None: - raise RuntimeError("should connect to director first before send data") + self.__check_socket_connection() msg_len = len(msg) # plus the msglen info try: @@ -96,8 +119,7 @@ class LowLevel(object): def recv(self): '''will receive data from director ''' - if self.socket == None: - raise RuntimeError("should connect to director first before recv data") + self.__check_socket_connection() # get the message header header = self.__get_header() if header <= 0: @@ -110,8 +132,7 @@ class LowLevel(object): def recv_msg(self): '''will receive data from director ''' - if self.socket == None: - raise RuntimeError("should connect to director first before recv data") + self.__check_socket_connection() msg = "" try: timeouts = 0 @@ -139,6 +160,7 @@ class LowLevel(object): self._handleSocketError(e) return msg + def recv_submsg(self, length): # get the message msg = "" @@ -154,6 +176,7 @@ class LowLevel(object): def __get_header(self): + self.__check_socket_connection() header = self.socket.recv(4) if len(header) == 0: self.logger.debug("received empty header, assuming connection is closed") @@ -267,6 +290,18 @@ class LowLevel(object): return True + def __check_socket_connection(self): + result = True + if self.socket == None: + result = False + if self.auth_credentials_valid: + # connection have worked before, but now it is gone + raise ConnectionLostError("currently no network connection") + else: + raise RuntimeError("should connect to director first before send data") + return result + + def _handleSocketError(self, exception): self.logger.error("socket error:" + str(exception)) self.socket = None diff --git a/bareos/exceptions.py b/bareos/exceptions.py index 1d79483..2602a00 100644 --- a/bareos/exceptions.py +++ b/bareos/exceptions.py @@ -14,6 +14,12 @@ class ConnectionError(Error): """ pass +class ConnectionLostError(Error): + """ + error with the Connection + """ + pass + class SocketEmptyHeader(Error): """ socket connection received an empty header. Connection lost? -- cgit v1.2.3