diff options
author | Joerg Steffens <joerg.steffens@bareos.com> | 2019-10-31 12:25:53 +0300 |
---|---|---|
committer | Joerg Steffens <joerg.steffens@bareos.com> | 2019-12-06 11:42:00 +0300 |
commit | 92d17dbabc5d5e2b5ef3d89c8a7c2ed526c77849 (patch) | |
tree | 1831b1758285697ba534947f686e9f058eaf3270 /python-bareos | |
parent | 1e0a32c9c58bcb8ccc402be8b3169882d26d1eed (diff) |
python-bareos: implement protocol >= 18.2
Also:
* Implement PAM authentication.
* Reduce warnings by closing socket, when they are no longer required.
* Prepare python-bareos version number. Fallback to 18.2.5, as this is the first version that uses the new protocol.
* Handle end of data in interactive mode.
* Adapted for connections to the Filedaemon.
Diffstat (limited to 'python-bareos')
-rw-r--r-- | python-bareos/README.rst | 51 | ||||
-rw-r--r-- | python-bareos/TODO.txt | 6 | ||||
-rw-r--r-- | python-bareos/bareos/__init__.py | 17 | ||||
-rw-r--r-- | python-bareos/bareos/bsock/__init__.py | 1 | ||||
-rw-r--r-- | python-bareos/bareos/bsock/constants.py | 10 | ||||
-rw-r--r-- | python-bareos/bareos/bsock/directorconsole.py | 65 | ||||
-rw-r--r-- | python-bareos/bareos/bsock/filedaemon.py | 21 | ||||
-rw-r--r-- | python-bareos/bareos/bsock/lowlevel.py | 177 | ||||
-rw-r--r-- | python-bareos/bareos/bsock/protocolmessageids.py | 19 | ||||
-rw-r--r-- | python-bareos/bareos/bsock/protocolmessages.py | 29 | ||||
-rw-r--r-- | python-bareos/bareos/bsock/protocolversions.py | 5 | ||||
-rwxr-xr-x | python-bareos/bin/bareos-fd-connect.py | 2 | ||||
-rwxr-xr-x | python-bareos/bin/bconsole-json.py | 11 | ||||
-rwxr-xr-x | python-bareos/bin/bconsole.py | 7 | ||||
-rw-r--r-- | python-bareos/setup.py | 30 |
15 files changed, 345 insertions, 106 deletions
diff --git a/python-bareos/README.rst b/python-bareos/README.rst index 53c50afb7..cbe75cbe9 100644 --- a/python-bareos/README.rst +++ b/python-bareos/README.rst @@ -3,7 +3,7 @@ python-bareos Python module to access a http://www.bareos.org backup system. -`python-bareos` packages are included in the Bareos core distribution since bareos >= 17.2. +`python-bareos` packages are included in the Bareos core distribution since Bareos >= 17.2. TLS-PSK ------- @@ -13,59 +13,62 @@ Since Bareos >= 18.2.4 can secure its connections via TLS-PSK (Transport-Layer-S This subset of TLS is currently not supported by the Python SSL module. To enable this feature in `python-bareos` the Python module 'sslpsk` must be installed: - * `pip install sslpsk` +.. code:: + pip install sslpsk -calling bareos-director user agent commands + +Calling bareos-director user agent commands ------------------------------------------- .. code:: python - import bareos.bsock + import bareos.bsock - password=bareos.bsock.Password('secret') - directorconsole=bareos.bsock.DirectorConsole(address='localhost', port=9101, password=password) - print directorconsole.call('help') - ... + password=bareos.bsock.Password('secret') + directorconsole=bareos.bsock.DirectorConsole(address='localhost', port=9101, password=password) + print directorconsole.call('help') + ... To connected to a named console instead, use the `name` parameter: .. code:: python - directorconsole=bareos.bsock.DirectorConsole(address='localhost', port=9101, name='user1', password=password) + password=bareos.bsock.Password('secret') + directorconsole=bareos.bsock.DirectorConsole(address='localhost', port=9101, name='user1', password=password) -simple version of the bconsole in Python --------------------------------------------- +Simple version of the bconsole in Python +---------------------------------------- .. code:: python - import bareos.bsock - password=bareos.bsock.Password('secret') - directorconsole=bareos.bsock.DirectorConsole(address='localhost', port=9101, password=password) - directorconsole.interactive() - ... + import bareos.bsock + password=bareos.bsock.Password('secret') + directorconsole=bareos.bsock.DirectorConsole(address='localhost', port=9101, password=password) + directorconsole.interactive() + ... or use the included bconsole.py script: .. - bconsole.py --debug --name=user1 --password=secret --port 9101 localhost + bconsole.py --debug --name=user1 --password=secret --port 9101 localhost -use JSON objects of API mode 2 +Use JSON objects of API mode 2 ---------------------------------- -Requires: bareos >= 15.2 +Requires: Bareos >= 15.2 .. code:: python - import bareos.bsock - password=bareos.bsock.Password('secret') - directorconsole=bareos.bsock.DirectorConsoleJson(address='localhost', port=9101, password=password) - directorconsole.call('list pools') - ... + import bareos.bsock + password=bareos.bsock.Password('secret') + directorconsole=bareos.bsock.DirectorConsoleJson(address='localhost', port=9101, password=password) + directorconsole.call('list pools') + ... diff --git a/python-bareos/TODO.txt b/python-bareos/TODO.txt deleted file mode 100644 index f7bc3b3b1..000000000 --- a/python-bareos/TODO.txt +++ /dev/null @@ -1,6 +0,0 @@ -TODO: - - * pyunit - -To test the Bareos Python module without installing, set PYTHONPATH accordingly, eg. -export PYTHONPATH=. diff --git a/python-bareos/bareos/__init__.py b/python-bareos/bareos/__init__.py index 6e6f32faf..d8c6a73ec 100644 --- a/python-bareos/bareos/__init__.py +++ b/python-bareos/bareos/__init__.py @@ -1,4 +1,21 @@ #__all__ = [ "bconsole" ] + +import os.path + +try: + base_dir = os.path.dirname(os.path.abspath(__file__)) +except NameError: + base_dir = None +else: + try: + with open(os.path.join(base_dir, 'VERSION.txt')) as version_file: + __version__ = version_file.read().strip() + except IOError: + # Fallback version. + # First protocol implemented + # has been introduced with this version. + __version__ = '18.2.5' + from bareos.exceptions import * from bareos.util.password import Password import bareos.util diff --git a/python-bareos/bareos/bsock/__init__.py b/python-bareos/bareos/bsock/__init__.py index 8984d21d3..111faf0de 100644 --- a/python-bareos/bareos/bsock/__init__.py +++ b/python-bareos/bareos/bsock/__init__.py @@ -1,4 +1,5 @@ #__all__ = [ "bconsole" ] + from bareos.exceptions import * from bareos.util.password import Password from bareos.bsock.connectiontype import ConnectionType diff --git a/python-bareos/bareos/bsock/constants.py b/python-bareos/bareos/bsock/constants.py index b4c0ba468..684942f5d 100644 --- a/python-bareos/bareos/bsock/constants.py +++ b/python-bareos/bareos/bsock/constants.py @@ -3,8 +3,14 @@ Constants used by Bareos. """ class Constants: + + unit_separator = chr(0x1f); + record_separator = chr(0x1e); + record_separator_compat_regex = r'[ {}]'.format(chr(0x1e)); + group_separator = chr(0x1d); + """ - translated enum from https://github.com/bareos/bareos/blob/master/src/lib/bsock.h + translated enum from https://github.com/bareos/bareos/blob/master/core/src/lib/bsock.h """ BNET_EOD = -1 #, /* End of data stream, new data may follow */ BNET_EOD_POLL = -2 #, /* End of data and poll all in one */ @@ -67,7 +73,7 @@ class Constants: } @staticmethod - def get_description( code ): + def get_description(code): try: description = Constants.description[code] except KeyError: diff --git a/python-bareos/bareos/bsock/directorconsole.py b/python-bareos/bareos/bsock/directorconsole.py index 3545fed56..1752b000c 100644 --- a/python-bareos/bareos/bsock/directorconsole.py +++ b/python-bareos/bareos/bsock/directorconsole.py @@ -2,8 +2,13 @@ Communicates with the bareos-dir console """ -from bareos.bsock.connectiontype import ConnectionType +from bareos.bsock.connectiontype import ConnectionType +from bareos.bsock.constants import Constants from bareos.bsock.lowlevel import LowLevel +from bareos.bsock.protocolmessageids import ProtocolMessageIds +from bareos.bsock.protocolmessages import ProtocolMessages +from bareos.bsock.protocolversions import ProtocolVersions +import bareos.exceptions class DirectorConsole(LowLevel): '''use to send and receive the response to Bareos File Daemon''' @@ -14,18 +19,74 @@ class DirectorConsole(LowLevel): dirname=None, name="*UserAgent*", password=None, + protocolversion=ProtocolVersions.last, + pam_username=None, + pam_password=None, tls_psk_enable=True, tls_psk_require=False ): super(DirectorConsole, self).__init__() + self.pam_username=pam_username + self.pam_password=pam_password self.tls_psk_enable=tls_psk_enable self.tls_psk_require=tls_psk_require self.identity_prefix = u'R_CONSOLE' + self.protocolversion = protocolversion + if self.protocolversion: + self.protocol_messages.set_version(self.protocolversion) self.connect(address, port, dirname, ConnectionType.DIRECTOR, name, password) - self.auth(name=name, password=password, auth_success_regex=b'^1000 OK.*$') + self.auth(name=name, password=password) self._init_connection() + def get_and_evaluate_auth_responses(self): + code, text = self.receive_and_evaluate_response_message() + + self.logger.debug(u'code: {}'.format(code)) + + # + # Test if PAM is requested. + # If yes, handle PAM messages. + # + # Is is only available, with protocolversion >= ProtocolVersions.bareos_18_2, + # however as it is optional, + # it will be evaluated with all protocol versions. + # + if code == ProtocolMessageIds.PamRequired: + self.logger.debug(u'PAM request: {}'.format(text)) + if (not self.pam_username) or (not self.pam_password): + raise bareos.exceptions.AuthenticationError("PAM authentication is requested, but no PAM credentials given. Giving up.\n"); + self.send(ProtocolMessages.pam_user_credentials(self.pam_username, self.pam_password)) + try: + code, text = self.receive_and_evaluate_response_message() + except bareos.exceptions.ConnectionLostError as e: + raise bareos.exceptions.AuthenticationError(u'PAM authentication failed.') + else: + if (self.pam_username) or (self.pam_password): + raise bareos.exceptions.AuthenticationError("PAM credentials provided, but this Director console does not offer PAM login. Giving up.\n"); + + + # + # Test if authentication has been accepted. + # + if code == ProtocolMessageIds.Ok: + self.logger.info(u'Authentication: {}'.format(text)) + self.auth_credentials_valid = True + else: + raise bareos.exceptions.AuthenticationError("Received unexcepted message: {} {} (expecting auth ok)".format(code, text)) + + + if self.protocolversion >= ProtocolVersions.bareos_18_2: + # + # Handle info message. + # + code, text = self.receive_and_evaluate_response_message() + if code == ProtocolMessageIds.InfoMessage: + self.logger.debug(u'Info: {}'.format(text)) + else: + raise bareos.exceptions.AuthenticationError("Received unexcepted message: {} {} (expecting info message)".format(code, text)) + + def _init_connection(self): self.call("autodisplay off") diff --git a/python-bareos/bareos/bsock/filedaemon.py b/python-bareos/bareos/bsock/filedaemon.py index 9f89d6de9..893419254 100644 --- a/python-bareos/bareos/bsock/filedaemon.py +++ b/python-bareos/bareos/bsock/filedaemon.py @@ -4,6 +4,8 @@ Communicates with the bareos-fd from bareos.bsock.connectiontype import ConnectionType from bareos.bsock.lowlevel import LowLevel +from bareos.bsock.protocolmessageids import ProtocolMessageIds +import bareos.exceptions import shlex @@ -26,9 +28,26 @@ class FileDaemon(LowLevel): # but using the interface provided for Directors. self.identity_prefix = u'R_DIRECTOR' self.connect(address, port, dirname, ConnectionType.FILEDAEMON, name, password) - self.auth(name=name, password=password, auth_success_regex=b'^2000 OK Hello.*$') + self.auth(name=name, password=password) self._init_connection() + + def get_and_evaluate_auth_responses(self): + code, text = self.receive_and_evaluate_response_message() + + self.logger.debug(u'code: {}'.format(code)) + + + # + # Test if authentication has been accepted. + # + if code == ProtocolMessageIds.FdOk: + self.logger.info(u'Authentication: {}'.format(text)) + self.auth_credentials_valid = True + else: + raise bareos.exceptions.AuthenticationError("Received unexcepted message: {} {} (expecting auth ok)".format(code, text)) + + def call(self, command): ''' Replace spaces by char(1) in quoted arguments diff --git a/python-bareos/bareos/bsock/lowlevel.py b/python-bareos/bareos/bsock/lowlevel.py index 522073300..b981022c4 100644 --- a/python-bareos/bareos/bsock/lowlevel.py +++ b/python-bareos/bareos/bsock/lowlevel.py @@ -20,7 +20,9 @@ import warnings from bareos.bsock.constants import Constants from bareos.bsock.connectiontype import ConnectionType +from bareos.bsock.protocolmessageids import ProtocolMessageIds from bareos.bsock.protocolmessages import ProtocolMessages +from bareos.bsock.protocolversions import ProtocolVersions from bareos.util.bareosbase64 import BareosBase64 from bareos.util.password import Password import bareos.exceptions @@ -46,6 +48,8 @@ class LowLevel(object): self.status = None self.address = None self.password = None + self.pam_username = None + self.pam_password = None self.port = None self.dirname = None self.socket = None @@ -53,10 +57,14 @@ class LowLevel(object): self.tls_psk_enable = True self.tls_psk_require = False self.connection_type = None + self.protocolversion = ProtocolVersions.last + self.protocol_messages = ProtocolMessages() # identity_prefix have to be set in each class self.identity_prefix = u'R_NONE' self.receive_buffer = b'' + def __del__(self): + self.close() def connect(self, address, port, dirname, type, name = None, password = None): self.address = address @@ -101,14 +109,13 @@ class LowLevel(object): self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # initialize try: - #self.socket = socket.create_connection((self.address, self.port)) self.socket.connect((self.address, self.port)) except (socket.error, socket.gaierror) as e: self._handleSocketError(e) raise bareos.exceptions.ConnectionError( "Failed to connect to host {}, port {}: {}".format(self.address, self.port, str(e))) - else: - self.logger.debug("connected to {}:{}".format(self.address, self.port)) + + self.logger.debug("connected to {}:{}".format(self.address, self.port)) return True @@ -124,14 +131,13 @@ class LowLevel(object): if isinstance(self.password, Password): password = self.password.md5() else: - raise bareos.exceptions.ConnectionError(u'No password provides.') + raise bareos.exceptions.ConnectionError(u'No password provided.') self.logger.debug("identity = {}, password = {}".format(identity, password)) try: self.socket = sslpsk.wrap_socket( client_socket, psk=(password, identity), ciphers='ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH', - #ssl_version=ssl.PROTOCOL_TLSv1_2, server_side=False) except ssl.SSLError as e: # raise ConnectionError( @@ -143,29 +149,41 @@ class LowLevel(object): def get_tls_psk_identity(self): '''Bareos TLS-PSK excepts the identiy is a specific format.''' - return u'{}{}{}'.format(self.identity_prefix, chr(0x1e), str(self.name)) + return u'{}{}{}'.format(self.identity_prefix, Constants.record_separator, str(self.name)) + - def is_tls_psk_available(self): + @staticmethod + def is_tls_psk_available(): '''Checks if we have all required modules for TLS-PSK.''' return 'sslpsk' in sys.modules - def auth(self, name, password, auth_success_regex): + + def auth(self, name, password): ''' - Login to the Bareos Director. - If the authenticate succeeds return True else False. - dir: the director name - name: own name. + Login to a Bareos Daemon. + + @param name: Own name. + @type name: str + + @param password: Password. + @type password: bareos.bsock.Password + + @return: True, if the authentication succeeds. + In earlier versions, authentication failures returned False. + However, now an authentication failure raises an exception. + @rtype: bool + + @raise bareos.exceptions.AuthenticationError: if authentication fails. ''' if not isinstance(password, Password): raise bareos.exceptions.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() def __auth(self): - bashed_name = ProtocolMessages.hello(self.name, type=self.connection_type) + bashed_name = self.protocol_messages.hello(self.name, type=self.connection_type) # send the bash to the director self.send(bashed_name) @@ -177,19 +195,32 @@ class LowLevel(object): raise bareos.exceptions.AuthenticationError("failed (in response)") if not self._cram_md5_challenge(clientname=self.name, password=self.password.md5(), tls_local_need=0, compatible=True): raise bareos.exceptions.AuthenticationError("failed (in challenge)") - self.recv_msg(self.auth_success_regex) - self.auth_credentials_valid = True - return True + + self.get_and_evaluate_auth_responses() + + return self.auth_credentials_valid + + + def receive_and_evaluate_response_message(self): + regex_str = r'^(\d\d\d\d){}(.*)$'.format(Constants.record_separator_compat_regex) + regex = bytes(bytearray(regex_str, 'utf8')) + incoming_message = self.recv_msg(regex) + match = re.search(regex, incoming_message, re.DOTALL) + code = int(match.group(1)) + text = match.group(2) + + return (code, text) def _init_connection(self): pass - def disconnect(self): + def close(self): '''disconnect''' - # TODO - pass + if self.socket: + self.socket.close() + self.socket = None def reconnect(self): @@ -221,7 +252,7 @@ class LowLevel(object): try: self.send(bytearray(command, 'utf-8')) result = self.recv_msg() - except (SocketEmptyHeader, bareos.exceptions.ConnectionLostError) as e: + except (bareos.exceptions.SocketEmptyHeader, bareos.exceptions.ConnectionLostError) as e: self.logger.error("connection problem (%s): %s" % (type(e).__name__, str(e))) if count == 0: if self.reconnect(): @@ -246,8 +277,43 @@ class LowLevel(object): self._handleSocketError(e) + def recv_bytes(self, length, timeout=10): + ''' + Receive a number of bytes. + + @raise bareos.exceptions.ConnectionLostError: + is raised, if the socket connection gets lost. + @raise socket.timeout: + is raised, if a timeout occurs on the socket connection, + meaning no data received. + ''' + self.socket.settimeout(timeout) + msg = b'' + # get the message + while length > 0: + self.logger.debug(" submsg len: " + str(length)) + submsg = self.socket.recv(length) + if len(submsg) == 0: + errormsg = u'Failed to retrieve data. Assuming the connection is lost.' + self._handleSocketError(errormsg) + raise bareos.exceptions.ConnectionLostError(errormsg) + length -= len(submsg) + msg += submsg + return msg + + def recv(self): - '''will receive data from director ''' + ''' + Receive a single Director message. + This is, + header (4 bytes): if + > 0: length of the following message + < 0: Bareos signal + msg: of the length descriped in the header. + + @raise bareos.exceptions.SignalReceivedException: + is raised, if a Bareos signal is received. + ''' self.__check_socket_connection() # get the message header header = self.__get_header() @@ -260,8 +326,18 @@ class LowLevel(object): return msg - def recv_msg(self, regex = b'^\d\d\d\d OK.*$', timeout = None): - '''will receive data from director ''' + def recv_msg(self, regex=b'^\d\d\d\d OK.*$', timeout=None): + ''' + Receive a full Director message. + + It retrieves Director messages (header + message text), + until + 1. the message matches the specified regex or + 2. the header indicates a signal. + + @raise bareos.exceptions.SignalReceivedException: + is raised, if a Bareos signal is received. + ''' self.__check_socket_connection() try: timeouts = 0 @@ -292,35 +368,25 @@ class LowLevel(object): # which might have been incomplete without new submsg. lastlineindex = self.receive_buffer.rfind(b'\n') + 1 self.receive_buffer += submsg - match = re.search(regex, self.receive_buffer[lastlineindex:], re.MULTILINE) + match = re.search(regex, self.receive_buffer[lastlineindex:], re.DOTALL) # Bareos indicates end of command result by line starting with 4 digits if match: self.logger.debug("msg \"{0}\" matches regex \"{1}\"".format(self.receive_buffer.strip(), regex)) result = self.receive_buffer[0:lastlineindex+match.end()] self.receive_buffer = self.receive_buffer[lastlineindex+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) def recv_submsg(self, length): # get the message - msg = b'' - while length > 0: - self.logger.debug(" submsg len: " + str(length)) - # TODO - self.socket.settimeout(10) - submsg = self.socket.recv(length) - length -= len(submsg) - #self.logger.debug(submsg) - msg += submsg + msg = self.recv_bytes(length) if (type(msg) is str): msg = bytearray(msg.decode('utf-8'), 'utf-8') if (type(msg) is bytes): msg = bytearray(msg) - #self.logger.debug(str(msg)) + self.logger.debug(str(msg)) return msg @@ -331,9 +397,17 @@ class LowLevel(object): """ command = "" while command != "exit" and command != "quit" and self.is_connected(): - command = self._get_input() - resultmsg = self.call(command) - self._show_result(resultmsg) + try: + command = self._get_input() + except EOFError: + return False + try: + resultmsg = self.call(command) + self._show_result(resultmsg) + except bareos.exceptions.JsonRpcErrorReceivedException as exp: + print(str(exp)) + #print(str(exp.jsondata)) + return True @@ -357,21 +431,8 @@ class LowLevel(object): def __get_header(self): - header = b'' - header_length = 4 - while header_length > 0: - self.logger.debug(" remaining header len: {0}".format(header_length)) - self.__check_socket_connection() - # TODO - self.socket.settimeout(10) - submsg = self.socket.recv(header_length) - header_length -= len(submsg) - header += submsg - if len(header) == 0: - self.logger.debug("received empty header, assuming connection is closed") - raise bareos.exceptions.SocketEmptyHeader() - else: - return self.__get_header_data(header) + header = self.recv_bytes(4) + return self.__get_header_data(header) def __get_header_data(self, header): @@ -505,7 +566,7 @@ class LowLevel(object): def __check_socket_connection(self): result = True - if self.socket == None: + if self.socket is None: result = False if self.auth_credentials_valid: # connection have worked before, but now it is gone @@ -516,5 +577,5 @@ class LowLevel(object): def _handleSocketError(self, exception): - self.logger.warn("socket error: {}".format(str(exception))) - self.socket = None + self.logger.warning("socket error: {}".format(str(exception))) + self.close() diff --git a/python-bareos/bareos/bsock/protocolmessageids.py b/python-bareos/bareos/bsock/protocolmessageids.py new file mode 100644 index 000000000..48cb3fe47 --- /dev/null +++ b/python-bareos/bareos/bsock/protocolmessageids.py @@ -0,0 +1,19 @@ +class ProtocolMessageIds(object): + ''' + From https://github.com/bareos/bareos/blob/master/core//src/lib/bnet.h + ''' + + # + # Director + # + Unknown = 0 + ProtokollError = 1 + ReceiveError = 2 + Ok = 1000 + PamRequired = 1001 + InfoMessage = 1002 + PamInteractive = 4001 + PamUserCredentials = 4002 + + # Filedaemon + FdOk = 2000 diff --git a/python-bareos/bareos/bsock/protocolmessages.py b/python-bareos/bareos/bsock/protocolmessages.py index 0db3642df..8c9a3d95b 100644 --- a/python-bareos/bareos/bsock/protocolmessages.py +++ b/python-bareos/bareos/bsock/protocolmessages.py @@ -2,18 +2,31 @@ Protocol messages between bareos-director and user-agent. """ +from bareos import __version__ from bareos.bsock.connectiontype import ConnectionType +from bareos.bsock.constants import Constants +from bareos.bsock.protocolmessageids import ProtocolMessageIds +from bareos.bsock.protocolversions import ProtocolVersions class ProtocolMessages(): """ strings defined by the protocol to talk to the Bareos Director. """ - @staticmethod - def hello(name, type=ConnectionType.DIRECTOR): + + def __init__(self, protocolversion = ProtocolVersions.last): + self.set_version(protocolversion) + + def set_version(self, protocolversion): + self.protocolversion = protocolversion + + def hello(self, name, type=ConnectionType.DIRECTOR): if type == ConnectionType.FILEDAEMON: return bytearray("Hello Director %s calling\n" % (name), 'utf-8') else: - return bytearray("Hello %s calling\n" % (name), 'utf-8') + if self.protocolversion < ProtocolVersions.bareos_18_2: + return bytearray("Hello %s calling\n" % (name), 'utf-8') + else: + return bytearray("Hello %s calling version %s\n" % (name, __version__), 'utf-8') #@staticmethod #def ok(): @@ -37,4 +50,12 @@ class ProtocolMessages(): @staticmethod def is_not_authorized(msg): - return msg == ProtocolMessages.not_authorized()
\ No newline at end of file + return msg == ProtocolMessages.not_authorized() + + @staticmethod + def pam_user_credentials(pam_username, pam_password): + ''' + Returns a string similar to: + 4002 USERNAME PASSWORD + ''' + return b'{id}{s}{username}{s}{password}'.format(id=ProtocolMessageIds.PamUserCredentials, username=pam_username, password=pam_password, s=Constants.record_separator) diff --git a/python-bareos/bareos/bsock/protocolversions.py b/python-bareos/bareos/bsock/protocolversions.py new file mode 100644 index 000000000..cf819159e --- /dev/null +++ b/python-bareos/bareos/bsock/protocolversions.py @@ -0,0 +1,5 @@ +class ProtocolVersions(object): + + bareos_12_4 = 1 + bareos_18_2 = 2 + last = 2 diff --git a/python-bareos/bin/bareos-fd-connect.py b/python-bareos/bin/bareos-fd-connect.py index 00a7fbb8a..bd505531f 100755 --- a/python-bareos/bin/bareos-fd-connect.py +++ b/python-bareos/bin/bareos-fd-connect.py @@ -7,7 +7,7 @@ import logging import sys def getArguments(): - parser = argparse.ArgumentParser(description='Connect to Bareos File Daemon.' ) + parser = argparse.ArgumentParser(description='Connect to Bareos File Daemon.') parser.add_argument('-d', '--debug', action='store_true', help="enable debugging output") parser.add_argument('--name', help="Name of the Director resource in the File Daemon", required=True) parser.add_argument('-p', '--password', help="password to authenticate to a Bareos File Daemon", required=True) diff --git a/python-bareos/bin/bconsole-json.py b/python-bareos/bin/bconsole-json.py index d04c7ab4c..fb242081b 100755 --- a/python-bareos/bin/bconsole-json.py +++ b/python-bareos/bin/bconsole-json.py @@ -3,6 +3,8 @@ from __future__ import print_function import argparse import bareos.bsock +import bareos.exceptions +from bareos.bsock.protocolversions import ProtocolVersions import logging import sys @@ -13,6 +15,9 @@ def getArguments(): parser.add_argument('-p', '--password', help="password to authenticate to a Bareos Director console", required=True) parser.add_argument('--port', default=9101, help="Bareos Director network port") parser.add_argument('--dirname', help="Bareos Director name") + parser.add_argument('--protocolversion', + default=ProtocolVersions.last, + help=u'Specify the protocol version to use. Default: {} (current)'.format(ProtocolVersions.last)) parser.add_argument('address', nargs='?', default="localhost", help="Bareos Director network address") args = parser.parse_args() return args @@ -37,7 +42,11 @@ if __name__ == '__main__': logger.debug('options: %s' % (parameter)) password = bareos.bsock.Password(args.password) parameter['password']=password - director = bareos.bsock.DirectorConsoleJson(**parameter) + try: + director = bareos.bsock.DirectorConsoleJson(**parameter) + except (bareos.exceptions.ConnectionError) as e: + print(str(e)) + sys.exit(1) except RuntimeError as e: print(str(e)) sys.exit(1) diff --git a/python-bareos/bin/bconsole.py b/python-bareos/bin/bconsole.py index 9e5dd9854..2dfe8f81b 100755 --- a/python-bareos/bin/bconsole.py +++ b/python-bareos/bin/bconsole.py @@ -2,9 +2,9 @@ from __future__ import print_function import argparse -#import bareos import bareos.bsock import bareos.exceptions +from bareos.bsock.protocolversions import ProtocolVersions import logging import sys @@ -15,6 +15,9 @@ def getArguments(): parser.add_argument('-p', '--password', help="password to authenticate to a Bareos Director console", required=True) parser.add_argument('--port', default=9101, help="Bareos Director network port") parser.add_argument('--dirname', help="Bareos Director name") + parser.add_argument('--protocolversion', + default=ProtocolVersions.last, + help=u'Specify the protocol version to use. Default: {} (current)'.format(ProtocolVersions.last)) parser.add_argument('address', nargs='?', default="localhost", help="Bareos Director network address") args = parser.parse_args() return args @@ -32,7 +35,7 @@ if __name__ == '__main__': parameter = {} for i in options: if hasattr(args, i) and getattr(args,i) != None: - logger.debug( "%s: %s" %(i, getattr(args,i))) + logger.debug("%s: %s" %(i, getattr(args,i))) parameter[i] = getattr(args,i) else: logger.debug( '%s: ""' %(i)) diff --git a/python-bareos/setup.py b/python-bareos/setup.py index 0eb78830d..06c950e8c 100644 --- a/python-bareos/setup.py +++ b/python-bareos/setup.py @@ -1,14 +1,32 @@ #!/usr/bin/python +import os from setuptools import find_packages, setup + +def get_version(): + base_dir = os.path.abspath(os.path.dirname(__file__)) + + try: + with open(os.path.join(base_dir, 'python-bareos', 'VERSION.txt')) as version_file: + __version__ = version_file.read().strip() + except IOError: + # Fallback version. + # First protocol implemented + # has been introduced with this version. + __version__ = '18.2.5' + + return __version__ + + setup( name='python-bareos', - version='0.3', - author='Joerg Steffens', - author_email='joerg.steffens@bareos.com', + version=get_version(), + author='Bareos Team', + author_email='packager@bareos.com', packages=find_packages(), scripts=['bin/bconsole.py', 'bin/bconsole-json.py', 'bin/bareos-fd-connect.py'], + package_data={'bareos': ['VERSION.txt']}, url='https://github.com/bareos/python-bareos/', # What does your project relate to? keywords='bareos', @@ -18,6 +36,8 @@ setup( #'hmac', #'socket', 'python-dateutil', - ] + ], + extras_require={ + 'TLS-PSK': ["sslpsk"], + } ) - |