# Copyright 1999-2016. Parallels IP Holdings GmbH. All Rights Reserved.
import os
import sys
import logging
from xml.dom import minidom
import pmm_config
import pmmcli_config
import pmm_dump_formatter
import pmm_api_xml_protocols
import subproc
import encodings.idna
from xml.parsers.expat import ExpatError

mswindows = (sys.platform == "win32")

pmm_ras_error_codes = {2: "Dump does not support partial import",
                       111: "Dump already exists in repository",
                       112: "Dump objects do not match with restore process owner",
                       113: "Unable to decrypt backup by specified key",
                       114: "Unable to convert dump to current format because it too old",
                       115: "Mail content was excluded from converted backup because it is in unsupported format. To keep mail content you have to create backup with 'dump restored to any mail server' mode.",
                       116: "Global server security settings prohibits restore from unsigned or modified backups. You can not restore from it, except your service provider explicitly allow it",
                       117: "Dump XML parsing error",
                       118: "You do not have enough privileges to import foreign dumps. It can be imported by your vendor",
                       121: "Permission denied error",
                       122: "FTP password does not match",
                       123: "FTP bad login",
                       124: "FTP host resolve error",
                       125: "FTP connection error",
                       126: "FTP network error",
                       127: "FTP dump file does not exists",
                       151: "Dump does not exist in repository",
                       152: "The file you are trying to upload is not a valid backup file",
                       153: "Dump already exists",
                       154: "Dump could not be extracted because of result path is too long"}

# Unknown error codes should be translated to error code = 199
pmm_ras_unknown_error_code = 199

converter_premature_eof_error_message = "The file you are trying to upload is not a valid backup file. \
Probably, it is a part of multivolume backup created from Plesk 8 or earlier, or it is simply broken."

_logger = logging.getLogger("pmmcli.pmm_repository_access_service")

class RepositoryAccessService(object):
    def __init__(self, session_path=None):
        self.isDebugOptionsAdded = False
        self._session_path = session_path if session_path is not None else pmm_config.pmm_logs_directory()

    def _get_pmmras_command(self, output=None):
        cmd = subproc.CmdLine(pmm_config.pmm_ras(), stdout=output)
        if pmmcli_config.get().force_debug_log() == 1:
            cmd.arg('--verbose')
            cmd.arg('--debug')
            self.isDebugOptionsAdded = True
        else:
            self.isDebugOptionsAdded = False
        return cmd

    def _exec_pmm_ras_command(self, exec_command, output=None):
        stdout = None
        errcode = 0
        errmessage = u''
        try:
            proc = exec_command.spawn()
            stdout = proc.stdout
        except subproc.NonzeroExitException, x:
            errcode = x.exitcode
            stdout = x.subprocess.stdout
            output_str = stdout
            if output:
                output.seek(0)
                output_str = output.read().decode('UTF-8', 'replace').strip()
            if errcode in pmm_ras_error_codes.keys():
                if (errcode == 117 or errcode == 118) and output_str:
                    errmessage = output_str
                else:
                    errmessage = pmm_ras_error_codes[errcode]
                x.sendNotification = False
            else:
                errmessage =  u"pmm-ras failed (Error code = " + unicode(errcode) + u"):"  \
                        + u" \nSTDOUT: " + output_str \
                        + u" \nSTDERR: " + x.subprocess.stderr
                errcode = pmm_ras_unknown_error_code
        return stdout, errcode, errmessage


class LocalRepositoryAccessService(RepositoryAccessService):
    def __init__(self, dumps_storage_credentials, session_path=None):
        super(LocalRepositoryAccessService, self).__init__(session_path)
        self._dumps_storage_credentials_formatter = pmm_dump_formatter.DumpsStorageCredentialsFormatter(dumps_storage_credentials)

    def _pmmras_get_child_dumps(self, dump_storage, dump_file, dst_dump_storage=None):
        # Method returns list of child dumps that make up whole dump
        password = self._dumps_storage_credentials_formatter.get_password()
        os.environ['DUMP_STORAGE_PASSWD'] = str(password)
        cmd = self._get_pmmras_command()
        cmd.arg('--get-child-dumps')
        dump_storage_arg = '--dump-storage=' + dump_storage
        cmd.arg(dump_storage_arg)
        if dst_dump_storage is not None:
            dst_dump_storage_arg = '--dst-dump-storage=' + dst_dump_storage
            cmd.arg(dst_dump_storage_arg)
        dump_file_specification_arg = '--dump-specification=' + dump_file
        cmd.arg(dump_file_specification_arg)

        session_path_arg = '--session-path=' + self._session_path
        cmd.arg(session_path_arg)

        if self._dumps_storage_credentials_formatter.get_use_passive_ftp_mode() == 'true':
            cmd.arg('--use-ftp-passive-mode')

        stdout, errcode, errmessage = self._exec_pmm_ras_command(cmd)
        if errcode != 0:
            errmessage = u"Unable to get child dumps: " + errmessage
        
        pmmras_dump_list = stdout.encode('utf-8')
        result = [line for line in pmmras_dump_list.split("\n") if line != '']
        return result, errcode, errmessage

    def _pmmras_get_dump_list_command_name(self):
        return '--get-dump-list'

    def _pmmras_fill_get_dumps_list_command_args(self, cmd, dump_storage, guid, id, name, type):
        if dump_storage is not None:
            dump_storage_arg = '--dump-storage=' + dump_storage
            cmd.arg(dump_storage_arg)
        if type:
            type_arg = '--type=' + type
            cmd.arg(type_arg)
        if guid:
            guid_arg = '--guid=' + guid
            cmd.arg(guid_arg)
        if id:
            id_arg = '--id=' + id
            cmd.arg(id_arg)
        if name:
            idna = name.find('xn--') >= 0
            if idna or type in ['client', 'reseller']:
                name_arg = '--name=' + name
            else:
                domain_names_parts = name.split('.')
                idna_name = ''
                for part in domain_names_parts:
                    if idna_name != '':
                        idna_name = idna_name + '.'
                    if part != '':
                        idna_name = idna_name + encodings.idna.ToASCII(part)
                name_arg = '--name=' + idna_name
            cmd.arg(name_arg)
        session_path_arg = '--session-path=' + self._session_path
        cmd.arg(session_path_arg)

    def _pmmras_get_dump_list(self, type, guid, id, name, dump_storage = None):
        # get pmmras dump list as XML string from pmmras
        # call pmm_ras, passing type, guid, id, name
        password = self._dumps_storage_credentials_formatter.get_password()
        os.environ['DUMP_STORAGE_PASSWD'] = str(password)
        cmd = self._get_pmmras_command()
        cmd.arg(self._pmmras_get_dump_list_command_name())
        self._pmmras_fill_get_dumps_list_command_args(cmd, dump_storage, guid, id, name, type)
        stdout, errcode, errmessage = self._exec_pmm_ras_command(cmd)
        dump_list = pmm_api_xml_protocols.DumpList.factory()

        if errcode != 0:
            errmessage = u"Unable to get dump list: " + errmessage
        else:
            try:
                pmmras_dump_list = stdout.encode('utf-8')
                dump_list.build(minidom.parseString(pmmras_dump_list).childNodes[0])
            except ExpatError:
                errmessage = u"Unable to parse dump list: ExpatError"
                errcode = pmm_ras_unknown_error_code
        
        return dump_list, errcode, errmessage

    def _pmmras_get_dump_status(self, dump_storage, dump_file):
        # get pmmras dump status as XML string from pmmras
        # call pmm_ras, passing secure dump file specification
        password = self._dumps_storage_credentials_formatter.get_password()
        os.environ['DUMP_STORAGE_PASSWD'] = str(password)
        cmd = self._get_pmmras_command()
        cmd.arg('--get-dump-info')
        dump_storage_arg = '--dump-storage=' + dump_storage
        cmd.arg(dump_storage_arg)
        dump_file_specification_arg = '--dump-file-specification=' + dump_file
        cmd.arg(dump_file_specification_arg)
        session_path_arg = '--session-path=' + self._session_path
        cmd.arg(session_path_arg)
        dump_result = None
        stdout, errcode, errmessage = self._exec_pmm_ras_command(cmd)

        if errcode != 0:
            errmessage = u"Unable to get dump status: " + errmessage
        else:
            dump_result = pmm_api_xml_protocols.Dump.factory()
            try:
                pmmras_dump_status = stdout.encode('utf-8')
                dump_result.build(minidom.parseString(pmmras_dump_status).childNodes[0])
            except ExpatError:
                errmessage = u"Unable to parse dump element: ExpatError"
                errcode = pmm_ras_unknown_error_code

        return dump_result, errcode, errmessage

    def _pmmras_fill_delete_dump_command_args(self, cmd, dump_file, dump_storage, object_specification):
        dump_storage_arg = '--dump-storage=' + dump_storage
        cmd.arg(dump_storage_arg)
        dump_file_specification_arg = '--dump-specification=' + dump_file
        cmd.arg(dump_file_specification_arg)
        if object_specification is not None:
            owner_guid_specification_arg = '--guid=' + object_specification.get_guid()
            cmd.arg(owner_guid_specification_arg)
        session_path_arg = '--session-path=' + self._session_path
        cmd.arg(session_path_arg)

    def _pmmras_delete_dump(self, dump_storage, dump_file, object_specification = None):
        # deletes dump
        # call pmm_ras, passing secure dump file specification
        password = self._dumps_storage_credentials_formatter.get_password()
        os.environ['DUMP_STORAGE_PASSWD'] = str(password)
        cmd = self._get_pmmras_command()
        cmd.arg('--delete-dump')
        self._pmmras_fill_delete_dump_command_args(cmd, dump_file, dump_storage, object_specification)
        stdout, errcode, errmessage = self._exec_pmm_ras_command(cmd)
        if errcode != 0:
            errmessage = u"Unable to delete dump: " + errmessage

        return errcode, errmessage

    def _pmmras_convert_local_dump(self, dump_storage, dump_file):
        cmd = self._get_pmmras_command()
        cmd.arg('--convert-local-dump')
        dump_storage_arg = '--dump-storage=' + dump_storage
        cmd.arg(dump_storage_arg)
        dump_specification_arg = '--dump-specification=' + dump_file
        cmd.arg(dump_specification_arg)
        session_path_arg = '--session-path=' + self._session_path
        cmd.arg(session_path_arg)
        stdout, errcode, errmessage = self._exec_pmm_ras_command(cmd)
        if errcode != 0:
            errmessage = u"Unable to convert local dump: " + errmessage

        return errcode, errmessage
        

    # the 'getDumpList' method returns XML-element-class instances autogenerated from 'pmm_api_xml_protocols.xsd'
    def getDumpList(self,object_specification, lightweight):
        dump_list, errcode, message = self._pmmras_get_dump_list(object_specification.get_type(),object_specification.get_guid(),object_specification.get_id(),object_specification.get_name(), self._dumps_storage_credentials_formatter.getFileDumpStorage())
        return dump_list, errcode, message

    # the 'getDumpStatus' method returns XML-element-class instances autogenerated from 'pmm_api_xml_protocols.xsd'
    def getDumpStatus(self,dump_specification):
        dump_specification_formatter = pmm_dump_formatter.DumpSpecificationFormatter(dump_specification)
        dump_file = dump_specification_formatter.get_name_of_xml_file()
        dump_storage = dump_specification_formatter.get_dumps_storage_credentials_formatter().getDumpStorage()
        dump_status, errcode, message = self._pmmras_get_dump_status(dump_storage, dump_file)
        return dump_status, errcode, message

    # the 'deleteDump' method returns error code and error message 
    def deleteDump(self, dump_specification, object_specification = None):
        dump_specification_formatter = pmm_dump_formatter.DumpSpecificationFormatter(dump_specification)
        dump_file = dump_specification_formatter.get_name_of_xml_file()
        dump_storage = dump_specification_formatter.get_dumps_storage_credentials_formatter().getDumpStorage()
        errcode, message = self._pmmras_delete_dump(dump_storage, dump_file, object_specification)
        return errcode, message

    def deleteIncompleteDumps(self, local_dump_name, session_path, dump_storage=None):
        cmdPmmRas = subproc.AsyncCmdLine(pmm_config.pmm_ras())
        cmdPmmRas\
            .arg('--delete-dump-files')\
            .arg('--dump-storage=' + (self._dumps_storage_credentials_formatter.get_root_dir() if dump_storage is None else dump_storage))\
            .arg('--dump-file-specification=' + local_dump_name)\
            .arg('--session-path=' + session_path)
        if pmmcli_config.get().force_debug_log() == 1:
            cmdPmmRas.arg('--verbose')
            cmdPmmRas.arg('--debug')
        cmdPmmRas.asyncSpawn()
        return_code = cmdPmmRas.get_return_code()
        if return_code is not None and 0 != return_code:
            _logger.info("Non-critical error. Unable to start pmm-ras '%s': %s" % (cmdPmmRas.get_cmd(), cmdPmmRas.get_stderr()))
        
    def convert_local_dump(self,dump_specification):
        dump_specification_formatter = pmm_dump_formatter.DumpSpecificationFormatter(dump_specification)
        dump_storage = dump_specification_formatter.get_dumps_storage_credentials_formatter().getDumpStorage()
        dump_file = dump_specification_formatter.get_name_of_xml_file()
        errcode, message = self._pmmras_convert_local_dump(dump_storage, dump_file)
        return errcode, message


class BackupNodeRepositoryAccessService(LocalRepositoryAccessService):
    def __init__(self, dumps_storage_credentials, session_path=None):
        super(BackupNodeRepositoryAccessService, self).__init__(dumps_storage_credentials, session_path)

    def _pmmras_get_dump_list_command_name(self):
        return '--get-backup-node-dump-list'

    def _pmmras_fill_get_dumps_list_command_args(self, cmd, dump_storage, guid, id, name, type):
        super(BackupNodeRepositoryAccessService, self)._pmmras_fill_get_dumps_list_command_args(cmd, dump_storage, guid, id, name, type)
        if self._dumps_storage_credentials_formatter.get_use_passive_ftp_mode() == 'true':
            cmd.arg('--use-ftp-passive-mode')

    def _pmmras_fill_delete_dump_command_args(self, cmd, dump_file, dump_storage, object_specification):
        super(BackupNodeRepositoryAccessService, self)._pmmras_fill_delete_dump_command_args(cmd, dump_file, dump_storage, object_specification)
        if self._dumps_storage_credentials_formatter.get_use_passive_ftp_mode() == 'true':
            cmd.arg('--use-ftp-passive-mode')


class PmmRasExecAccessService(RepositoryAccessService):
    def _pmmras_exec(self, params, env, output_file=None):
        if env:
            for name, val in env.iteritems():
                os.environ[str(name)] = str(val)

        output = open(output_file, 'w+b') if output_file else None

        try:
            cmd = self._get_pmmras_command(output)

            for param in params:
                if (self.isDebugOptionsAdded == True and (param == '--debug' or param == '--verbose')):
                    continue
                cmd.arg(param)

            stdout, errcode, errmessage = self._exec_pmm_ras_command(cmd, output)

            return errcode, errmessage, stdout
        finally:
            if output:
                output.close()


class FtpRepositoryAccessService(RepositoryAccessService):
    def __init__(self, dumps_storage_credentials, session_path=None):
        super(FtpRepositoryAccessService, self).__init__(session_path)
        self.__dumps_storage_credentials_formatter = pmm_dump_formatter.DumpsStorageCredentialsFormatter(dumps_storage_credentials)

    # for the case of migration functionality to PMM RAS
    def _pmmras_export_dump_as_file(self, dump_file_name, dump_storage_credentials_formatter, password, include_increments):
        # dump_storage_credentials_formatter is a target DumpsStorageCredentialsFormatter
        if not mswindows:
            raise Exception("pmm-ras --export_dump_as_file is supported for Windows only!")
        
        os.environ['DUMP_STORAGE_PASSWD'] = str(password)
        cmd = self._get_pmmras_command()
        cmd.arg('--export-dump-as-file')
        
        if dump_storage_credentials_formatter.get_use_passive_ftp_mode() == 'true':
            cmd.arg('--use-ftp-passive-mode')
        
        # dump specification is a filename relative for local dumps storage
        dump_specification_arg = '--dump-specification=' + dump_file_name
        cmd.arg(dump_specification_arg)
        dump_file_specification_arg = '--dump-file-specification=' + dump_storage_credentials_formatter.getFileDumpStorage()
        cmd.arg(dump_file_specification_arg)
        session_path_arg = '--session-path=' + self._session_path
        cmd.arg(session_path_arg)

        if include_increments:
            cmd.arg('--include-increments')

        filename = None
        stdout, errcode, errmessage = self._exec_pmm_ras_command(cmd)
        if errcode == 0 or errcode == 111:
            filename = stdout.strip()
        else:
            errmessage = u"Unable to export dump as file: " + errmessage

        return errcode, errmessage, filename

    # download to local repository is supported only. so far pmm-ras does not need to know repository password
    def _pmmras_import_file_as_dump(self, dump_file, dumps_storage, guid,type, ignore_backup_password, ignore_content_list_sign, objectsTreeFile, config_only=False, content_only=False):
        # call pmm_ras, passing secure_source_dump_file_specification,source_password,secure_dump_storage
        password = self.__dumps_storage_credentials_formatter.get_password()
        os.environ['DUMP_STORAGE_PASSWD'] = str(password)
        cmd = self._get_pmmras_command()
        cmd.arg('--import-file-as-dump')
        
        if self.__dumps_storage_credentials_formatter.get_use_passive_ftp_mode() == 'true':
            cmd.arg('--use-ftp-passive-mode')

        if objectsTreeFile:
            cmd.arg('--objects-tree-file=' + objectsTreeFile)
        
        dump_file_specification_arg = '--dump-file-specification=' + dump_file
        cmd.arg(dump_file_specification_arg)
        dump_storage_arg = '--dump-storage=' + dumps_storage
        cmd.arg(dump_storage_arg)
        guid_arg = '--guid=' + guid
        cmd.arg(guid_arg)
        type_arg = '--type=' + type
        cmd.arg(type_arg)
        session_path_arg = '--session-path=' + self._session_path
        cmd.arg(session_path_arg)
        if ignore_backup_password:
            cmd.arg('--allow-reset-passwords')
        cmd.arg('--check-sign')
        if ignore_content_list_sign == 'true':
            cmd.arg('--force')
        if config_only:
            cmd.arg('--config-only')
        if content_only:
            cmd.arg('--content-only')
        filename = None

        stdout, errcode, errmessage = self._exec_pmm_ras_command(cmd)
        if errcode == 0:
            filenames = stdout.splitlines()
            if len(filenames) > 1:
                filename = filenames[-1].strip()
            else:
                filename = filenames[0].strip()
        elif errcode == 111 or errcode == 113 or errcode == 116:
            filename = stdout.strip()
        elif errcode == 154:
            pass
        else:
            errmessage = u"Unable to import file as dump: " + errmessage
        
        return errcode, errmessage, filename
    

    def _pmmras_export_file_as_file(self, destination_dump, destination_dumps_storage_password, join_volumes):
        source_dump = self.__dumps_storage_credentials_formatter.getFileDumpStorage()
        source__dumps_storage_password = self.__dumps_storage_credentials_formatter.get_password()
        os.environ['DUMP_STORAGE_PASSWD_SRC'] = str(source__dumps_storage_password)
        os.environ['DUMP_STORAGE_PASSWD_DST'] = str(destination_dumps_storage_password)
        cmd = self._get_pmmras_command()
        cmd.arg('--export-file-as-file')
        
        if self.__dumps_storage_credentials_formatter.get_use_passive_ftp_mode() == 'true':
            cmd.arg('--use-ftp-passive-mode')
        
        file_from_arg = '--file-from=' + source_dump
        cmd.arg(file_from_arg)
        file_to_arg = '--file-to=' + destination_dump
        cmd.arg(file_to_arg)
        session_path_arg = '--session-path=' + self._session_path
        cmd.arg(session_path_arg)
        if join_volumes == True:
            cmd.arg("--join-volumes")

        stdout, errcode, errmessage = self._exec_pmm_ras_command(cmd)
        if errcode != 0:
            errmessage = u"Unable to export file as file: " + errmessage
        
        return errcode, errmessage

    def _pmmras_get_ftp_dump_list(self, lightweight):
        password = self.__dumps_storage_credentials_formatter.get_password()
        secure_dumps_storage = self.__dumps_storage_credentials_formatter.getDumpStorage()
        os.environ['DUMP_STORAGE_PASSWD'] = str(password)
        pmmras_dump_list = None
        cmd = self._get_pmmras_command()
        cmd.arg('--get-ftp-dump-list')
        
        if self.__dumps_storage_credentials_formatter.get_use_passive_ftp_mode() == 'true':
            cmd.arg('--use-ftp-passive-mode')

        if lightweight:
            cmd.arg('--lightweight-mode')
        
        dump_storage_arg = '--dump-storage=' + secure_dumps_storage
        cmd.arg(dump_storage_arg)
        session_path_arg = '--session-path=' + self._session_path
        cmd.arg(session_path_arg)
        dump_list = pmm_api_xml_protocols.DumpList.factory()

        stdout, errcode, errmessage = self._exec_pmm_ras_command(cmd)
        if errcode != 0:
            errmessage = u"Unable to get ftp dump list: " + errmessage
        else:
            try:
                pmmras_dump_list = stdout.encode('utf-8')
                dump_list.build(minidom.parseString(pmmras_dump_list).childNodes[0])
            except ExpatError:
                errmessage = u"Unable to parse ftp dump list: ExpatError"
                errcode = pmm_ras_unknown_error_code

        return dump_list, errcode, errmessage

    def _pmmras_get_dump_status(self,dump_storage,dump_file):
        # get pmmras_dump_status as XML string from pmmras
        # call pmm_ras, passing secure dump file specification
        password = self.__dumps_storage_credentials_formatter.get_password()
        os.environ['DUMP_STORAGE_PASSWD'] = str(password)
        pmmras_dump_status = None
        cmd = self._get_pmmras_command()
        cmd.arg('--get-ftp-dump-info')
        
        if self.__dumps_storage_credentials_formatter.get_use_passive_ftp_mode() == 'true':
            cmd.arg('--use-ftp-passive-mode')
        
        dump_storage_arg = '--dump-storage=' + dump_storage
        cmd.arg(dump_storage_arg)
        dump_file_specification_arg = '--dump-file-specification=' + dump_file
        cmd.arg(dump_file_specification_arg)
        session_path_arg = '--session-path=' + self._session_path
        cmd.arg(session_path_arg)
        dump_result = None

        stdout, errcode, errmessage = self._exec_pmm_ras_command(cmd)
        if errcode != 0:
            errmessage = u"Unable to get dump status: " + errmessage
        else:
            pmmras_dump_status = stdout.encode('utf-8')
            dump_result = pmm_api_xml_protocols.Dump.factory()
            try:
                dump_result.build(minidom.parseString(pmmras_dump_status).childNodes[0])
            except ExpatError:
                errmessage = u"Unable to parse dump element: ExpatError"
                errcode = pmm_ras_unknown_error_code
        
        return dump_result, errcode, errmessage

    def _pmmras_delete_dump(self, dump_storage, dump_file):
        # deletes dump
        password = self.__dumps_storage_credentials_formatter.get_password()
        os.environ['DUMP_STORAGE_PASSWD'] = str(password)
        cmd = self._get_pmmras_command()
        cmd.arg('--delete-exported-dump')
        
        if self.__dumps_storage_credentials_formatter.get_use_passive_ftp_mode() == 'true':
            cmd.arg('--use-ftp-passive-mode')
        
        dump_storage_arg = '--dump-storage=' + dump_storage
        cmd.arg(dump_storage_arg)
        dump_file_specification_arg = '--dump-file-specification=' + dump_file
        cmd.arg(dump_file_specification_arg)
        session_path_arg = '--session-path=' + self._session_path
        cmd.arg(session_path_arg)
        stdout, errcode, errmessage = self._exec_pmm_ras_command(cmd)
        if errcode != 0:
            errmessage = u"Unable to delete dump: " + errmessage

        return errcode, errmessage

    def _pmmras_check_ftp_repository(self):
        cmd = self._get_pmmras_command()
        password = self.__dumps_storage_credentials_formatter.get_password()
        cmd.var('DUMP_STORAGE_PASSWD', password)
        cmd.arg('--check-repository')
        secure_dumps_storage = self.__dumps_storage_credentials_formatter.getDumpStorage()
        dump_storage_arg = '--dump-storage=' + secure_dumps_storage
        cmd.arg(dump_storage_arg)
        print self.__dumps_storage_credentials_formatter.get_use_passive_ftp_mode()
        if self.__dumps_storage_credentials_formatter.get_use_passive_ftp_mode() == 'true':
            cmd.arg('--use-ftp-passive-mode')
        stdout, errcode, errmessage = self._exec_pmm_ras_command(cmd)
        if errcode != 0:
            errmessage = u"Unable to check ftp repository: " + errmessage

        return errcode, errmessage

    # the 'import_file_as_dump' method returns errcode, message
    # pmm-ras supports only 'local' storage-type in destination_dump_specification
    def import_file_as_dump(self, source_dump_specification, destination_dump_specification, guid, type, ignore_backup_password, objectsTreeFile, config_only=False, content_only=False):
        source_dump_specification_formatter = pmm_dump_formatter.DumpSpecificationFormatter(source_dump_specification)
        destination_dump_specification_formatter = pmm_dump_formatter.DumpSpecificationFormatter(destination_dump_specification)
        dump_file = source_dump_specification_formatter.get_destination_file()
        dumps_storage = destination_dump_specification_formatter.get_dumps_storage_credentials_formatter().getDumpStorage()
        ignore_backup_sign = destination_dump_specification.get_dumps_storage_credentials_formatter().get_ignore_backup_sign()
        return self._pmmras_import_file_as_dump(
            dump_file, dumps_storage, guid, type, ignore_backup_password, ignore_backup_sign, objectsTreeFile, config_only, content_only)

    # the 'export_dump_as_file' method returns errcode, message
    def export_dump_as_file(self,source_dump_specification,destination_dump_specification,include_increments):
        source_dump_specification_formatter = pmm_dump_formatter.DumpSpecificationFormatter(source_dump_specification)
        dump_file_name = source_dump_specification_formatter.get_name_of_xml_file()
        destination_dump_specification_formatter = pmm_dump_formatter.DumpSpecificationFormatter(destination_dump_specification)
        dumps_storage_credentials_formatter = destination_dump_specification_formatter.get_dumps_storage_credentials_formatter()
        destination_dumps_storage_password = dumps_storage_credentials_formatter.get_password()
        errcode, message, filename = self._pmmras_export_dump_as_file(dump_file_name,dumps_storage_credentials_formatter,destination_dumps_storage_password,include_increments)
        return errcode, message, filename

    # the 'export_file_as_file' method returns errcode, message
    def export_file_as_file(self,destination_dump_specification, join_volumes):
        destination_dump_specification_formatter = pmm_dump_formatter.DumpSpecificationFormatter(destination_dump_specification)
        destination_dump = destination_dump_specification_formatter.get_destination_file()
        destination_dumps_storage_password = destination_dump_specification_formatter.get_dumps_storage_credentials_formatter().get_password()
        errcode, message = self._pmmras_export_file_as_file(destination_dump, destination_dumps_storage_password, join_volumes)
        return errcode, message

    # the 'getDumpList' method returns XML-element-class instances autogenerated from 'pmm_api_xml_protocols.xsd'
    def getDumpList(self,object_specification, lightweight):
        # object specification is ignored for ftp dumps, pmm-ras shows all dump files
        dump_list, errcode, message = self._pmmras_get_ftp_dump_list(lightweight)
        return dump_list, errcode, message

    # the 'getDumpStatus' method returns XML-element-class instances autogenerated from 'pmm_api_xml_protocols.xsd'
    def getDumpStatus(self,dump_specification):
        dump_specification_formatter = pmm_dump_formatter.DumpSpecificationFormatter(dump_specification)
        dump_storage_credentials_formatter = dump_specification_formatter.get_dumps_storage_credentials_formatter()
        dump_storage = dump_storage_credentials_formatter.getDumpStorage()
        dump_file = dump_storage_credentials_formatter.get_file_name()
        dump_status, errcode, message = self._pmmras_get_dump_status(dump_storage,dump_file)
        return dump_status, errcode, message

    def deleteIncompleteDumps(self, dump_name, session_path):
        password = self.__dumps_storage_credentials_formatter.get_password()
        os.environ['DUMP_STORAGE_PASSWD'] = str(password)
        cmdPmmRas = subproc.AsyncCmdLine(pmm_config.pmm_ras())
        cmdPmmRas\
            .arg('--delete-dump-files')\
            .arg('--dump-storage=' + self.__dumps_storage_credentials_formatter.getDumpStorage())\
            .arg('--dump-file-specification=' + dump_name)\
            .arg('--session-path=' + session_path)
        if pmmcli_config.get().force_debug_log() == 1:
            cmdPmmRas.arg('--verbose')
            cmdPmmRas.arg('--debug')
        cmdPmmRas.asyncSpawn()
        return_code = cmdPmmRas.get_return_code()
        if return_code is not None and 0 != return_code:
            _logger.info("Non-critical error. Unable to start pmm-ras '%s': %s" % (cmdPmmRas.get_cmd(), cmdPmmRas.get_stderr()))

    # the 'deleteDump' method returns error code and error message
    def deleteDump(self,dump_specification, object_specification = None):
        dump_specification_formatter = pmm_dump_formatter.DumpSpecificationFormatter(dump_specification)
        dump_storage_credentials_formatter = dump_specification_formatter.get_dumps_storage_credentials_formatter()
        dump_file = dump_storage_credentials_formatter.get_file_name()
        dump_storage = dump_storage_credentials_formatter.getDumpStorage()
        errcode, message = self._pmmras_delete_dump(dump_storage, dump_file)
        return errcode, message

    def checkRepository(self):
        return self._pmmras_check_ftp_repository()
