Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/bareos/python-bareos.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'bareos/bsock/lowlevel.py')
-rw-r--r--bareos/bsock/lowlevel.py129
1 files changed, 111 insertions, 18 deletions
diff --git a/bareos/bsock/lowlevel.py b/bareos/bsock/lowlevel.py
index 644c3de..5e5fd38 100644
--- a/bareos/bsock/lowlevel.py
+++ b/bareos/bsock/lowlevel.py
@@ -14,8 +14,11 @@ from bareos.bsock.protocolmessages import ProtocolMessages
import hmac
import logging
import random
+import re
+from select import select
import socket
import struct
+import sys
import time
class LowLevel(object):
@@ -34,12 +37,9 @@ class LowLevel(object):
self.socket = None
self.auth_credentials_valid = False
self.connection_type = None
+ self.receive_buffer = b''
-
- def connect(self, address="localhost", port=9101, dirname=None, type=ConnectionType.DIRECTOR):
- '''
- connect to bareos-director
- '''
+ def connect(self, address, port, dirname, type):
self.address = address
self.port = port
if dirname:
@@ -62,17 +62,18 @@ class LowLevel(object):
return True
- def auth(self, password, name="*UserAgent*"):
+ def auth(self, name, password, auth_success_regex):
'''
login to the bareos-director
if the authenticate success return True else False
dir: the director location
- name: own name. Default is *UserAgent*
+ name: own name.
'''
if not isinstance(password, Password):
raise AuthenticationError("password must by of type bareos.Password() not %s" % (type(password)))
self.password = password
self.name = name
+ self.auth_success_regex = auth_success_regex
return self.__auth()
@@ -86,10 +87,15 @@ class LowLevel(object):
raise AuthenticationError("failed (in response)")
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.recv_msg(self.auth_success_regex)
self.auth_credentials_valid = True
return True
+ def _init_connection(self):
+ pass
+
+
def disconnect(self):
''' disconnect '''
# TODO
@@ -100,13 +106,43 @@ class LowLevel(object):
result = False
if self.auth_credentials_valid:
try:
- if self.__connect() and self.__auth() and self._set_state_director_prompt():
+ if self.__connect() and self.__auth() and self._init_connection():
result = True
except socket.error:
self.logger.warning("failed to reconnect")
return result
+ def call(self, command):
+ '''
+ call a bareos-director user agent command
+ '''
+ if isinstance(command, list):
+ command = " ".join(command)
+ return self.__call(command, 0)
+
+
+ def __call(self, command, count):
+ '''
+ Send a command and receive the result.
+ If connection is lost, try to reconnect.
+ '''
+ result = b''
+ try:
+ self.send(bytearray(command, 'utf-8'))
+ 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)
+
+
def send(self, msg=None):
'''use socket to send request to director'''
self.__check_socket_connection()
@@ -133,10 +169,9 @@ class LowLevel(object):
return msg
- def recv_msg(self):
+ def recv_msg(self, regex = b'^\d\d\d\d OK.*$', timeout = None):
'''will receive data from director '''
self.__check_socket_connection()
- msg = b""
try:
timeouts = 0
while True:
@@ -154,11 +189,22 @@ class LowLevel(object):
# header is a signal
self.__set_status(header)
if self.is_end_of_message(header):
- return msg
+ result = self.receive_buffer
+ self.receive_buffer = b''
+ return result
else:
# header is the length of the next message
length = header
- msg += self.recv_submsg(length)
+ self.receive_buffer += self.recv_submsg(length)
+ # Bareos indicates end of command result by line starting with 4 digits
+ match = re.search(regex, self.receive_buffer, re.MULTILINE)
+ if match:
+ self.logger.debug("msg \"{0}\" matches regex \"{1}\"".format(self.receive_buffer.strip(), regex))
+ result = self.receive_buffer[0:match.end()]
+ self.receive_buffer = self.receive_buffer[match.end()+1:]
+ return result
+ #elif re.search("^\d\d\d\d .*$", msg, re.MULTILINE):
+ #return msg
except socket.error as e:
self._handleSocketError(e)
return msg
@@ -166,7 +212,7 @@ class LowLevel(object):
def recv_submsg(self, length):
# get the message
- msg = b""
+ msg = b''
while length > 0:
self.logger.debug(" submsg len: " + str(length))
# TODO
@@ -179,9 +225,41 @@ class LowLevel(object):
msg = bytearray(msg.decode('utf-8'), 'utf-8')
if (type(msg) is bytes):
msg = bytearray(msg)
+ #self.logger.debug(str(msg))
return msg
+ def interactive(self):
+ """
+ Enter the interactive mode.
+ Exit via typing "exit" or "quit".
+ """
+ command = ""
+ while command != "exit" and command != "quit" and self.is_connected():
+ command = self._get_input()
+ resultmsg = self.call(command)
+ self._show_result(resultmsg)
+ return True
+
+
+ def _get_input(self):
+ # Python2: raw_input, Python3: input
+ try:
+ myinput = raw_input
+ except NameError:
+ myinput = input
+ data = myinput(">>")
+ return data
+
+
+ def _show_result(self, msg):
+ #print(msg.decode('utf-8'))
+ sys.stdout.write(msg.decode('utf-8'))
+ # add a linefeed, if there isn't one already
+ if msg[-2] != ord(b'\n'):
+ sys.stdout.write(b'\n')
+
+
def __get_header(self):
self.__check_socket_connection()
header = self.socket.recv(4)
@@ -201,11 +279,17 @@ class LowLevel(object):
def is_end_of_message(self, data):
- return (data == Constants.BNET_EOD or
+ return ((not self.is_connected()) or
+ data == Constants.BNET_EOD or
+ data == Constants.BNET_TERMINATE or
data == Constants.BNET_MAIN_PROMPT or
data == Constants.BNET_SUB_PROMPT)
+ def is_connected(self):
+ return (self.status != Constants.BNET_TERMINATE)
+
+
def _cram_md5_challenge(self, clientname, password, tls_local_need=0, compatible=True):
'''
client launch the challenge,
@@ -246,6 +330,7 @@ class LowLevel(object):
# check the response is equal to base64
return is_correct
+
def _cram_md5_respond(self, password, tls_remote_need=0, compatible=True):
'''
client connect to dir,
@@ -291,10 +376,18 @@ class LowLevel(object):
self.logger.debug(str(status_text) + " (" + str(status) + ")")
- def _set_state_director_prompt(self):
- self.send(b".")
- msg = self.recv_msg()
- self.logger.debug("received message: " + str(msg))
+ def has_data(self):
+ self.__check_socket_connection()
+ timeout = 0.1
+ readable, writable, exceptional = select([self.socket], [], [], timeout)
+ return readable
+
+
+ def get_to_prompt(self):
+ time.sleep(0.1)
+ if self.has_data():
+ msg = self.recv_msg()
+ self.logger.debug("received message: " + str(msg))
# TODO: check prompt
return True