import os
import sys
import time
import logging
import libxml2
import cStringIO
import stacktrace
from xml.dom import minidom
from xml.parsers.expat import ExpatError

import pmm_config
import migration_handler_session
from migration_handler_restore import MigrationRestore, MigrationRestoreException
from pmm_api_xml_protocols import Response, MigrationTaskDescription, MigrationObjectList, Data, TaskLog, \
ConflictResolutionRules, IPMapping, OwnersMapping, DBMapping, TaskList, Task, TaskStatus, TaskStatusMixed, TaskStatusRestore, \
Working, Finished, Starting, SessionList, DstTemporaryDirectory, ScoutResult
from pmm_migr_remote_service import MigrationRemoteService
from pmm_task import getPMMTaskManager
import pmmcli_exceptions
from execution_result import ExecutionResult, ExecutionResultRestore, MessageType, ExecutionResultMixed

import log
import osutil
import subproc
import dirutil
import validator
from encoder import EncoderFile


# errcodes are dictionary with number as key, tuple as value: ('translated error code','errcode message', 'use stdout flag', 'use stderr flag')}
win_errcodes = { -3: (3003, None, True, False),
                 -1: (3001, None, True, False),
                 -4: (3004, None, True, False),
                 11: (3011, 'The migration agent is locked by a failed migration task or is in use by another migration process. Please restart migration agent on the source server.', False, False),
                 12: (3012, 'Connection to host is not available or migration agent is not running on the source host', False, False),
                 10: (3010, 'Unable to connect with remote agent (connection to host is not available)', False, False),
                 13: (3013, 'Unable to deploy scout to unix host. Please check the ssh connection with the source host', False, False),
                 14: (3014, 'Unable to deploy agent to unix host. Ssh connection failed', False, False),
                 15: (3015, 'Wrong login or password', False, False),
                 16: (3016, 'Specified login does not have administrator privileges', False, False) }

unix_errcodes = {11: (3011, 'Connection refused: No one listening on the specified address and port', False, False),
                 12: (3012, 'Connection timeout', False, False),
                 13: (3013, 'Network is unreachable', False, False),
                 14: (3014, 'No route to host', False, False),
                 15: (3015, 'Connection failed', True, False),
                 16: (3016, 'Unable to resolve host name: Unknown host', False, False),
                 17: (3017, 'Incorrect user name or password specified', False, False),
                 18: (3018, 'Ssh connection failed', False, False) }


trace = True


_logger = logging.getLogger("pmm.migration_handler")


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

errcodes = unix_errcodes
if mswindows:
    errcodes = win_errcodes


libxml2errorHandlerErr = ''


def libxml2errorHandler(ctx, str_):
    global libxml2errorHandlerErr
    libxml2errorHandlerErr = libxml2errorHandlerErr + "%s %s" % (ctx, str_)


class MigrationHandlerException(Exception):
    def __init__(self, message):
        Exception.__init__(self, message)


class MigrationHandlerActionParamException(MigrationHandlerException):
    def __init__(self, action_processor, message):
        self.caller = action_processor
        self.message = message
        MigrationHandlerException.__init__(self, message)

    def get_message(self):
        return self.message


class MigrationHandlerActionRunner(object):
    def __init__(self, ActionProcessor,parameter_stdin = None, parameters = None):
        self.processor = ActionProcessor(parameter_stdin, parameters)

    def doActivity(self):
        libxml2.registerErrorHandler(libxml2errorHandler, '')
        input_valid, msg = self.processor.validate()
        if not input_valid:
            _logger.error("Validate failed: ActionProcessor " + str(self.processor))
            raise MigrationHandlerActionParamException(self.processor, msg)
        result = None
        _logger.debug(str(self) + ": doActivity")
        result = self.processor.doActivity()
        return self.processor.response(result)


class MigrationHandlerAction(object):
    def __init__(self, parameter_stdin, parameters):
        _logger.debug("--> " + str(self))
        if parameters is not None:
            _logger.info("parameters: " + str(parameters))
        if parameter_stdin is not None:
            _logger.info("stdin: " + parameter_stdin)
        self._stdin = parameter_stdin
        self._params = parameters
        self._process = None
        self.__input_validator = validator.Validator(pmm_config.pmm_api_xml_protocols_schema())

    def get_input_validator(self):
        return self.__input_validator

    def validate(self):
        _logger.debug(str(self) + ": validate")
        return 1

    def doActivity(self):
        raise MigrationHandlerException('Could not call doActivity method of base class.')

    def response(self, result):
        _logger.debug(str(self) + ": response")
        return result

    def setProcess(self, process):
        if process == None:
            _logger.debug("Subprocess finished")
        else:
            _logger.debug(u"Subprocess " + unicode(process) + u" encountered.")
            self._process = process

    def unsetProcess(self, process):
        self.setProcess(None)

    def parse_lauchpaderror(self, exceptionObject ):
        raw_errcode = exceptionObject.exitcode
        stdout = exceptionObject.subprocess.stdout
        stderr = exceptionObject.subprocess.stderr          
        message =  None
        # errcode = 1000 is default 'subprocess fault' errcode  
        errcode = 1000
        if raw_errcode in errcodes:
            errcode, err_message, use_stdout, use_stderr = errcodes[raw_errcode]
            if err_message is not None:
                message = err_message
            elif use_stdout:
                if stdout != "":
                    message = stdout
            elif use_stderr:
                if stderr != "":
                    message = stderr
        if message is None:
            message =  u"launchpad error (Error code = " + unicode(raw_errcode) + "):\n"
            if stdout!="":
                message += u"== STDOUT ====================\n" + stdout + u"\n==============================\n" 
            if stderr!="":
                message += u"== STDERR ====================\n" + stderr + u"\n==============================\n"
        
        return errcode, message


class MigrationGetScoutInfoAction(MigrationHandlerAction):
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameter_stdin = parameter_stdin
        self.__migration_task_description = None

    def validate(self):
        try:
            error_code, msg = self.get_input_validator().do(self.__parameter_stdin)
        except libxml2.parserError, ex:
            return None, 'XML parse error: ' + ex.msg + '\n' + libxml2errorHandlerErr
        if error_code:
            return None, 'Error ' + str(error_code) + ': ' + msg
        try:
            self.__migration_task_description = MigrationTaskDescription.factory()
            self.__migration_task_description.build(minidom.parseString(self.__parameter_stdin).childNodes[0])
        except ExpatError:
            return None, 'ExpatError in MigrationGetScoutInfo'
        return 1, ''

    def doActivity(self):
        if self.__migration_task_description is None:
            self.__migration_task_description = MigrationTaskDescription.factory()
            self.__migration_task_description.build(minidom.parseString(self.__parameter_stdin).childNodes[0])
        
        owner_guid = self.__migration_task_description.get_owner_guid()
        agent_credentials = self.__migration_task_description.get_agent_credentials()
        dst_host_parameters = self.__migration_task_description.get_dst_host_parameters()
        dst_temporary_directory_object = dst_host_parameters.get_dst_temporary_directory()
        
        migration_session = migration_handler_session.getMigrationSessionManager().addSession(agent_credentials)
        
        migration_session.setValue('owner_guid', owner_guid)
        
        if dst_temporary_directory_object is not None:
            dst_temporary_directory_base = dst_temporary_directory_object.getValueOf_()
            if dst_temporary_directory_base != '':
                dst_temporary_directory_base_abs = os.path.abspath(dst_temporary_directory_base)
                dst_temporary_directory = os.path.join(dst_temporary_directory_base_abs,migration_session.getSessionId())
                try:
                    dirutil.mkdirs(dst_temporary_directory, 0755)
                except Exception, e:
                    _logger.error("Could not initialize dst_temporary_directory '%s'" % dst_temporary_directory)
                else:
                    migration_session.setValue('dst_temporary_directory', dst_temporary_directory)
        
        host_credentials = agent_credentials.get_host_credentials()
        hostname = host_credentials.get_hostname()
        service = MigrationRemoteService.getService(hostname, host_credentials.get_port(), host_credentials.get_login(), host_credentials.get_password(), migration_session)
        
        system_type = 'windows'
        remoting_type = host_credentials.get_remoting_type()
        if remoting_type is None or remoting_type in ['','ssh']:
            system_type = 'unix'
        
        log_directory = agent_credentials.get_log_directory()
        if log_directory is None or log_directory == '':
            log_directory = '/migration'
        migration_session.setValue('source_host', hostname )
        migration_session.setValue('agent_dir',os.path.join( log_directory, migration_session.getSessionId() ) )
        
        try:
            service.sendScout(system_type)
        except subproc.NonzeroExitException, x:
            _logger.error("Launchpad could not send scout: %s" % unicode(x))
            errcode, message = self.parse_lauchpaderror( x )
            return None, errcode, message
        migration_session.setValue('step', 'deploy-scout' )
        
        scout_result_object = None
        try:
            scout_result_object = service.getScoutResult()
            migration_session.setObject('scout_result', scout_result_object )
            os_info = scout_result_object.get_os_info()
            source_os = os_info.get_os()
            if source_os is None or source_os == '':
                source_os = system_type
            migration_session.setValue('source_os', source_os )
        except subproc.NonzeroExitException, x:
            _logger.error("Launchpad could not get scout result: %s" % unicode(x))
            errcode, message = self.parse_lauchpaderror( x )
            return None, errcode, message
        except ExpatError, x:
            _logger.error(u"Xml reading error. Could not parse ScoutResult: %s" % unicode(x))
            scout_result_object = ScoutResult.factory()
        
        scout_result_object.set_migration_session_id( migration_session.getSessionId() )
        
        data = Data.factory()
        data.set_scout_result( scout_result_object )
        
        return data, 0, ''


class MigrationGetObjectsListAction(MigrationHandlerAction):
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameter_stdin = parameter_stdin
        self.__parameters = parameters
        self.__migration_session_id = None
        self.__agent_to_use = None

    def validate(self):
        if len(self.__parameters) != 1:
            return None, "Parameter 'migration-session-id' is not specified"
        self.__migration_session_id = self.__parameters[0]
        # there is no format requirements for migration-session-id
        
        self.__agent_to_use = self.__parameter_stdin
        if self.__agent_to_use is None or self.__agent_to_use == '':
            return None, "Parameter 'agent-to-use' is not specified"
        return 1, ''

    def __makeData(self, objects_list_object):
        data = Data.factory()
        if objects_list_object is None:
            objects_list_object = MigrationObjectList.factory()
        objects_list_object.set_migration_session_id( self.__migration_session_id )
        data.set_migration_objects_list(objects_list_object)
        return data

    def doActivity(self):
        if self.__migration_session_id is None:
            self.__migration_session_id = self.__parameters[0]
        if self.__agent_to_use is None:
            self.__agent_to_use = self.__parameter_stdin
        
        migration_session = migration_handler_session.MigrationSession( self.__migration_session_id )
        
        if migration_session.getValue('agent_name') == self.__agent_to_use:
            objects_list_object = migration_session.getObject('objects_list')
            if objects_list_object is not None:
                return self.__makeData(objects_list_object), 0, ''
        
        migration_session.setValue('agent_name',self.__agent_to_use)
        agent_dir = migration_session.getValue('agent_dir')
        
        scout_result_object = migration_session.getObject('scout_result')
        
        if scout_result_object is None:
            return None, 4, "Migration session does not contain ScoutResult"
        
        detected_panels_list = scout_result_object.get_detected_panel()
        source_panel_object = None
        if detected_panels_list is None or len(detected_panels_list) == 0:
            return None, 3000, 'Specified platform is not found on source host'
        _logger.debug("Platform selected: '%s'" % self.__agent_to_use)
        for detected_panel_item in detected_panels_list:
            agent_name = detected_panel_item.get_agent()
            _logger.debug("Platform detected: '%s', agent: '%s'" % (detected_panel_item.get_name(),agent_name))
            if agent_name == self.__agent_to_use:
                source_panel_object = detected_panel_item
                migration_session.setValue('platform_name',detected_panel_item.get_name())
                break
        if source_panel_object is None:
            return None, 3000, 'Specified platform is not found on source host'
        
        agent_credentials_object = migration_session.getObject('agent_credentials')
        host_credentials_object = agent_credentials_object.get_host_credentials()
        service = MigrationRemoteService.getService(host_credentials_object.get_hostname(),
                                                    host_credentials_object.get_port(),
                                                    host_credentials_object.get_login(),
                                                    host_credentials_object.get_password(),
                                                    migration_session)

        try:
            service.deployAgent(self.__agent_to_use, agent_dir)
        except subproc.NonzeroExitException, x:
            _logger.error("Launchpad could not deploy agent: %s" % unicode(x))
            errcode, message = self.parse_lauchpaderror( x )
            return None, errcode, message
        
        migration_session.setValue('step','deploy-agent')
        
        objects_list_object = None
        
        try:
            objects_list_object = service.getObjectsList(self.__agent_to_use, agent_dir)
            migration_session.setObject('objects_list', objects_list_object)
        except ExpatError, x:
            _logger.error(u"Xml reading error. Could not parse ObjectsList: %s" % unicode(x))
        
        migration_session.setValue('step','get-accounts')
        
        try:
            service.clearStatus()
        except subproc.NonzeroExitException, x:
            _logger.error(u"Could not clear launchpad status: %s" % unicode(x))
        
        return self.__makeData(objects_list_object), 0, ''


class MigrationSetIPMappingAction(MigrationHandlerAction):
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameter_stdin = parameter_stdin
        self.__parameters = parameters
        self.__migration_session_id = None
        self.__ip_mapping = None

    def validate(self):
        if len(self.__parameters) != 1:
            return None, "Parameter 'migration-session-id' is not specified"
        self.__migration_session_id = self.__parameters[0]
        # there is no format requirements for migration-session-id
        try:
            error_code, msg = self.get_input_validator().do(self.__parameter_stdin)
        except libxml2.parserError, ex:
            return None, 'XML parse error: ' + ex.msg + '\n' + libxml2errorHandlerErr
        if error_code:
            return None, 'Error ' + str(error_code) + ': ' + msg
        try:
            self.__ip_mapping = IPMapping.factory()
            self.__ip_mapping.build(minidom.parseString(self.__parameter_stdin).childNodes[0])
        except ExpatError:
            return None, 'ExpatError in MigrationSetIPMapping'
        return 1, ''

    def doActivity(self):
        if self.__ip_mapping is None:
            self.__ip_mapping = IPMapping.factory()
            self.__ip_mapping.build(minidom.parseString(self.__parameter_stdin).childNodes[0])
        if self.__migration_session_id is None:
            self.__migration_session_id = self.__parameters[0]
        
        session = migration_handler_session.MigrationSession( self.__migration_session_id )
        session.setObject('ip_mapping', self.__ip_mapping )
        return None, 0, ''


class MigrationGetIPMappingAction(MigrationHandlerAction):
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameters = parameters
        self.__migration_session_id = None

    def validate(self):
        if len(self.__parameters) != 1:
            return None, "Parameter 'migration-session-id' is not specified"
        self.__migration_session_id = self.__parameters[0]
        return 1, ''

    def doActivity(self):
        if self.__migration_session_id is None:
            self.__migration_session_id = self.__parameters[0]
        
        session = migration_handler_session.MigrationSession( self.__migration_session_id )
        
        migration_ip_mapping_object = session.getObject('ip_mapping')
        if migration_ip_mapping_object is None:
            return None, 4, "Migration session does not contain IpMapping"
        
        data = Data( migration_ip_mapping = migration_ip_mapping_object)
        return data, 0, ''


class MigrationSetDBMappingAction(MigrationHandlerAction):            
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameter_stdin = parameter_stdin
        self.__parameters = parameters
        self.__migration_session_id = None
        self.__db_mapping = None

    def validate(self):
        if len(self.__parameters) != 1:
            return None, "Parameter 'migration-session-id' is not specified"
        self.__migration_session_id = self.__parameters[0]
        # there is no format requirements for migration-session-id
        try:
            error_code, msg = self.get_input_validator().do(self.__parameter_stdin)
        except libxml2.parserError, ex:
            return None, 'XML parse error: ' + ex.msg + '\n' + libxml2errorHandlerErr
        if error_code:
            return None, 'Error ' + str(error_code) + ': ' + msg
        try:
            self.__db_mapping = DBMapping.factory()
            self.__db_mapping.build(minidom.parseString(self.__parameter_stdin).childNodes[0])
        except ExpatError:
            return None, 'ExpatError in MigrationSetDBMapping'
        return 1, ''

    def doActivity(self):
        if self.__db_mapping is None:
            self.__db_mapping = DBMapping.factory()
            self.__db_mapping.build(minidom.parseString(self.__parameter_stdin).childNodes[0])
        if self.__migration_session_id is None:
            self.__migration_session_id = self.__parameters[0]
        
        session = migration_handler_session.MigrationSession( self.__migration_session_id )
        session.setObject('db_mapping', self.__db_mapping)
        return None, 0, ''


class MigrationGetDBMappingAction(MigrationHandlerAction):            
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameters = parameters
        self.__migration_session_id = None

    def validate(self):
        if len(self.__parameters) != 1:
            return None, "Parameter 'migration-session-id' is not specified"
        self.__migration_session_id = self.__parameters[0]
        return 1, ''

    def doActivity(self):
        if self.__migration_session_id is None:
            self.__migration_session_id = self.__parameters[0]
        
        session = migration_handler_session.MigrationSession( self.__migration_session_id )
        
        migration_db_mapping_object = session.getObject('db_mapping')
        if migration_db_mapping_object is None:
            return None, 4, "Migration session does not contain DbMapping"
        
        data = Data( migration_db_mapping = migration_db_mapping_object)
        return data, 0, ''


class MigrationSetOwnerMappingAction(MigrationHandlerAction):
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameter_stdin = parameter_stdin
        self.__parameters = parameters
        self.__migration_session_id = None
        self.__owner_mapping = None

    def validate(self):
        if len(self.__parameters) != 1:
            return None, "Parameter 'migration-session-id' is not specified"
        self.__migration_session_id = self.__parameters[0]
        try:
            error_code, msg = self.get_input_validator().do(self.__parameter_stdin)
        except libxml2.parserError, ex:
            return None, 'XML parse error: ' + ex.msg + '\n' + libxml2errorHandlerErr
        if error_code:
            return None, 'Error ' + str(error_code) + ': ' + msg
        try:
            self.__owner_mapping = OwnersMapping.factory()
            self.__owner_mapping.build(minidom.parseString(self.__parameter_stdin).childNodes[0])
        except ExpatError:
            return None, 'ExpatError in MigrationSetOwnerMappingAction'
        return 1, ''

    def doActivity(self):
        if self.__owner_mapping is None:
            self.__owner_mapping = OwnersMapping.factory()
            self.__owner_mapping.build(minidom.parseString(self.__parameter_stdin).childNodes[0])
        if self.__migration_session_id is None:
            self.__migration_session_id = self.__parameters[0]
        
        session = migration_handler_session.MigrationSession( self.__migration_session_id )
        session.setObject('owner_mapping', self.__owner_mapping)
        return None, 0, ''


class MigrationGetOwnerMappingAction(MigrationHandlerAction):
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameters = parameters
        self.__migration_session_id = None

    def validate(self):
        if len(self.__parameters) != 1:
            return None, "Parameter 'migration-session-id' is not specified"
        self.__migration_session_id = self.__parameters[0]
        return 1, ''

    def doActivity(self):
        if not self.__migration_session_id :
            self.__migration_session_id = self.__parameters[0]
        
        session = migration_handler_session.MigrationSession( self.__migration_session_id )
        
        migration_owners_object = session.getObject('owner_mapping')
        if migration_owners_object is None:
            return None, 4, "Migration session does not contain OwnerMapping"
        
        data = Data( migration_owners = migration_owners_object)
        return data, 0, ''


class MigrationGetDstTemporaryDirectoryAction(MigrationHandlerAction):
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameters = parameters
        self.__migration_session_id = None

    def validate(self):
        if len(self.__parameters) != 1:
            return None, "Parameter 'migration-session-id' is not specified"
        self.__migration_session_id = self.__parameters[0]
        return 1, ''

    def doActivity(self):
        if self.__migration_session_id is None:
            self.__migration_session_id = self.__parameters[0]
        
        session = migration_handler_session.MigrationSession( self.__migration_session_id )
        
        dst_temporary_directory_value = session.getValue('dst_temporary_directory')
        if dst_temporary_directory_value is None:
            return None, 4, "Migration session does not contain DstTemporaryDirectory"
        dst_temporary_directory_object = DstTemporaryDirectory.factory()
        dst_temporary_directory_object.setValueOf_(dst_temporary_directory_value)
        data = Data( dst_temporary_directory = dst_temporary_directory_object)
        return data, 0, ''


class MigrationSetRestorePolicyAction(MigrationHandlerAction):
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameter_stdin = parameter_stdin
        self.__parameters = parameters
        self.__migration_session_id = None
        self.__conflict_resolution_rules = None

    def validate(self):
        if len(self.__parameters) != 1:
            return None, "Parameter 'migration-session-id' is not specified"
        self.__migration_session_id = self.__parameters[0]
        # there is no format requirements for migration-session-id
        try:
            error_code, msg = self.get_input_validator().do(self.__parameter_stdin)
        except libxml2.parserError, ex:
            return None, 'XML parse error: ' + ex.msg + '\n' + libxml2errorHandlerErr
        if error_code:
            return None, 'Error ' + str(error_code) + ': ' + msg
        try:
            self.__conflict_resolution_rules = ConflictResolutionRules.factory()
            self.__conflict_resolution_rules.build(minidom.parseString(self.__parameter_stdin).childNodes[0])
        except ExpatError:
            return None, 'ExpatError in MigrationSetRestorePolicy'
        return 1, ''

    def doActivity(self):
        if self.__conflict_resolution_rules is None:
            self.__conflict_resolution_rules = ConflictResolutionRules.factory()
            self.__conflict_resolution_rules.build(minidom.parseString(self.__parameter_stdin).childNodes[0])
        if self.__migration_session_id is None:
            self.__migration_session_id = self.__parameters[0]
        
        session = migration_handler_session.MigrationSession( self.__migration_session_id )
        session.setObject('restore_policy', self.__conflict_resolution_rules)
        return None, 0, ''


class MigrationGetRestorePolicyAction(MigrationHandlerAction):
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameters = parameters
        self.__migration_session_id = None

    def validate(self):
        if len(self.__parameters) != 1:
            return None, "Parameter 'migration-session-id' is not specified"
        self.__migration_session_id = self.__parameters[0]
        return 1, ''

    def doActivity(self):
        if self.__migration_session_id is None:
            self.__migration_session_id = self.__parameters[0]
        
        session = migration_handler_session.MigrationSession( self.__migration_session_id )
        
        migration_restore_policy_object = session.getObject('restore_policy')
        if migration_restore_policy_object is None:
            return None, 4, "Migration session does not contain RestorePolicy"
        
        data = Data( migration_restore_policy = migration_restore_policy_object)
        return data, 0, ''


class MigrationGetSelectedObjectsAction(MigrationHandlerAction):
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameters = parameters
        self.__migration_session_id = None

    def validate(self):
        if len(self.__parameters) != 1:
            return None, "Parameter 'migration-session-id' is not specified"
        self.__migration_session_id = self.__parameters[0]
        return 1, ''

    def doActivity(self):
        if self.__migration_session_id is None:
            self.__migration_session_id = self.__parameters[0]
        
        session = migration_handler_session.MigrationSession( self.__migration_session_id )
        
        objects_list = session.getObject('selected_objects')
        if objects_list is None:
            return None, 4, "Migration session does not contain SelectedObjectsList"
        
        objects_list.set_migration_session_id(session.getSessionId())
        data = Data.factory( migration_selected_objects = objects_list)
        return data, 0, ''


class MigrationGetSessionObjectsListAction(MigrationHandlerAction):
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameters = parameters
        self.__migration_session_id = None

    def validate(self):
        if len(self.__parameters) != 1:
            return None, "Parameter 'migration-session-id' is not specified"
        self.__migration_session_id = self.__parameters[0]
        return 1, ''

    def doActivity(self):
        if self.__migration_session_id is None:
            self.__migration_session_id = self.__parameters[0]
        
        session = migration_handler_session.MigrationSession( self.__migration_session_id )
        
        objects_list = session.getObject('objects_list')
        if objects_list is None:
            return None, 4, "Migration session does not contain ObjectsList"
        
        objects_list.set_migration_session_id(session.getSessionId())
        data = Data( migration_objects_list = objects_list)
        return data, 0, ''


class MigrationGetAgentCredentialsAction(MigrationHandlerAction):
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameters = parameters
        self.__migration_session_id = None

    def validate(self):
        if len(self.__parameters) != 1:
            return None, "Parameter 'migration-session-id' is not specified"
        self.__migration_session_id = self.__parameters[0]
        return 1, ''

    def doActivity(self):
        if self.__migration_session_id is None:
            self.__migration_session_id = self.__parameters[0]
        
        session = migration_handler_session.MigrationSession( self.__migration_session_id )
        
        agent_credentials_object = session.getObject('agent_credentials')
        if agent_credentials_object is None:
            return None, 4, "Migration session does not contain AgentCredentials"
        
        data = Data( agent_credentials = agent_credentials_object)
        return data, 0, ''


class MigrationGetIPForMappingAction(MigrationHandlerAction):
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameters = parameters
        self.__migration_session_id = None

    def validate(self):
        if len(self.__parameters) != 1:
            return None, "Parameter 'migration-session-id' is not specified"
        self.__migration_session_id = self.__parameters[0]
        return 1, ''

    def doActivity(self):
        if self.__migration_session_id is None:
            self.__migration_session_id = self.__parameters[0]
        
        session = migration_handler_session.MigrationSession( self.__migration_session_id )
        
        ip_for_mapping_object = session.getObject('ip_for_mapping')
        if ip_for_mapping_object is None:
            return None, 4, "Migration session does not contain IP for mapping"
        
        data = Data( migration_ip_mapping = ip_for_mapping_object )
        return data, 0, ''


class MigrationGetIPForMappingObjectsAction(MigrationHandlerAction):
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameter_stdin = parameter_stdin
        self.__parameters = parameters
        self.__migration_session_id = None
        self.__selected_objects = None

    def validate(self):
        if len(self.__parameters) != 1:
            return None, "Parameter 'migration-session-id' is not specified"
        self.__migration_session_id = self.__parameters[0]
        
        # check for scout_result is got already
        session = migration_handler_session.MigrationSession( self.__migration_session_id )
        scout_result_object = session.getObject('scout_result')
        if scout_result_object is None:
            return None, "Command '--migration-get-scout-info' was not called before '--migration-get-ip-for-mapping-objects'"
        
        try:
            self.__selected_objects = MigrationObjectList.factory()
            self.__selected_objects.build(minidom.parseString(self.__parameter_stdin).childNodes[0])
        except ExpatError:
            return None, 'ExpatError in MigrationGetIPForMappingAction'
        return 1, ''

    def doActivity(self):
        if self.__migration_session_id is None:
            self.__migration_session_id = self.__parameters[0]
        if self.__selected_objects is None:
            self.__selected_objects = MigrationObjectList.factory()
            self.__selected_objects.build(minidom.parseString(self.__parameter_stdin).childNodes[0])
        
        data = None
        errcode = 0
        message = None
        try:
            session = migration_handler_session.MigrationSession( self.__migration_session_id )
            session.setObject('selected_objects', self.__selected_objects)
            migration_parameters = self.__selected_objects.get_parameters()
            if migration_parameters is not None:
                dump_transformer_options = migration_parameters.get_dump_transformer_options()
                if dump_transformer_options is not None:
                    session.setObject('dump_transformer_options', dump_transformer_options)
            
            session.setValue('step', 'get-ip-for-map')
            
            agent_credentials = session.getObject('agent_credentials')
            host_credentials = agent_credentials.host_credentials
            
            service = MigrationRemoteService.getService(host_credentials.hostname, host_credentials.port, host_credentials.login, host_credentials.password, session)
            
            # when MigrationParameters is specified - check for presence of needed package-installed in scout result
            migration_parameters_object = self.__selected_objects.get_parameters()
            if migration_parameters_object is not None:
                scout_result_object = session.getObject('scout_result')
                check_error_code, check_error_message = service.checkMigrationParameters(host_credentials.hostname, migration_parameters_object, scout_result_object)
                if check_error_code != 0:
                    return None, check_error_code, check_error_message
            
            service.selectAccounts(session.getSelectedObjectsPath())
            
            ipMap = service.getIpMap(session.getSelectedObjectsPath())
            session.setObject('ip_for_mapping', ipMap)
            data = Data.factory()
            data.migration_ip_mapping = ipMap
        except subproc.NonzeroExitException, x:
            errcode, message = self.parse_lauchpaderror( x )
        except ExpatError, x:
            errcode = 1000
            message =  u"Xml reading error: " + unicode(x)
        
        return data, errcode, message


class MigrationTaskStartAction(MigrationHandlerAction):
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameters = parameters
        self.__migration_session_id = None

    def validate(self):
        if len(self.__parameters) != 1:
            return None, "Parameter 'migration-session-id' is not specified"
        self.__migration_session_id = self.__parameters[0]
        # there are no format requirements for migration-session-id
        return 1, ''

    def doActivity(self):
        if self.__migration_session_id is None:
            self.__migration_session_id = self.__parameters[0]
        
        # try to initialize migration_session
        migration_session = migration_handler_session.MigrationSession(self.__migration_session_id)
        
        migration_session.setValue('step', 'start-migration')
        
        migration_task_id = 0
        cmd = subproc.AsyncCmdLine(pmm_config.migration_handler())
        for migration_handler_arg in pmm_config.migration_handler_args():
            cmd.arg(migration_handler_arg)
        
        migration_start_arg = '--migration-start'
        cmd.arg(migration_start_arg)
        
        migration_session_id_arg = self.__migration_session_id
        cmd.arg(migration_session_id_arg)

        cmd.var('PLESK_MIGRATION_MODE', '1')

        data = None
        errcode = 0
        errmsg = None
        try:
            cmd.asyncSpawn()
            pid = cmd.pid
            migration_session.setMigrationProcess(pid)
            errmsg = cmd.get_cmd()
            _logger.debug("Start migration process: %s" % errmsg)
            
            migration_task_id = pid
            migration_session.setValue('migration_task_id',migration_task_id)
            
            additional_info_object = migration_session.getAdditionalInfo()
            
            task_list_object = TaskList.factory()
            
            #this status is workaround to comply with protocol's schema
            status = TaskStatus.factory( working = Working.factory( starting = Starting.factory() ) )
            task = Task.factory( task_id = pid, 
                                 task_status = status,
                                 additional_info = additional_info_object)
            
            task_list_object.add_task(task)
            
            data = Data.factory( task_list = task_list_object )
            
        except subproc.AsyncExecuteException, x:
            errcode = 1000
            errmsg = u"Exception raised while invoke 'migration-handler' utility: %s" % unicode(x)
        
        return data, errcode, errmsg


class MigrationStartAction(MigrationHandlerAction):
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameters = parameters
        self.__migration_session_id = None

    def validate(self):
        if len(self.__parameters) != 1:
            return None, "Parameter 'migration-session-id' is not specified"
        self.__migration_session_id = self.__parameters[0]
        # there is no format requirements for migration-session-id
        return 1, ''

    def doActivity(self):
        if self.__migration_session_id is None:
            self.__migration_session_id = self.__parameters[0]
        
        migration_session = migration_handler_session.MigrationSession(self.__migration_session_id)
        # pass self.__migration_session_id to migration_handler_launchpad
        # work with migration_handler_launchpad
        agent_credentials = migration_session.getObject('agent_credentials')
        service = MigrationRemoteService.getService(agent_credentials.get_host_credentials().get_hostname(),
                                                    agent_credentials.get_host_credentials().get_port(),
                                                    agent_credentials.get_host_credentials().get_login(),
                                                    agent_credentials.get_host_credentials().get_password(),
                                                    migration_session)
        migration_session.setFlag('migration_task_started')
        service.backup(migration_session.getValue('agent_name'), migration_session.getValue('agent_dir'))
        _logger.debug("migration dump successfully created: %s" % migration_session.getValue('dump-name'))
        
        service.removeScout()
        
        migration_session.setValue('step','restore')
        restore_service = MigrationRestore(self.__migration_session_id)
        # Catch up restore exceptions.
        # Mark restore as finished with error if any.
        restore_task_id = None
        try:
            response_data_task_id, restore_task_id = restore_service.beginRestore()
            if response_data_task_id is not None:
                self.__setRestoreTaskId(migration_session, response_data_task_id)
                restore_task_id = response_data_task_id
            elif restore_task_id is not None:
                self.__setRestoreTaskId(migration_session, restore_task_id)
                restore_service.restore()
        except MigrationRestoreException, x:
            restore_task_id = x.get_task_id()
            self.__setRestoreFailed(migration_session, x.get_message(), restore_task_id)
            if (restore_task_id is not None):
                self.__setRestoreTaskId(migration_session, restore_task_id)
            raise
        except pmmcli_exceptions.PMMUtilityException, x:
            self.__setRestoreFailed(migration_session, str(x))
            raise
        except Exception, e:
            self.__setRestoreFailed(migration_session, str(e))
            raise

        if restore_task_id is None:
            errmsg = "Could not get restore task id for migration session '%s'" % self.__migration_session_id
            self.__setRestoreFailed(migration_session, errmsg)
            return None, 1, errmsg

        try:
            # poll restore task and save task status
            while True:
                time.sleep(10)
                cmd = subproc.CmdLine(pmm_config.pmmcli())
                cmd.arg('--get-task-status')
                cmd.arg(restore_task_id)

                proc = cmd.spawn()
                get_task_status_result = proc.stdout.encode('utf-8')
                response = Response.factory()
                response.build(minidom.parseString(get_task_status_result).childNodes[0])
                data = response.get_data()
                if data is None:
                    errmsg = "Could not get restore task status from '%s' result:\n%s" % (cmd.get_cmd(), get_task_status_result)
                    self.__setRestoreFailed(migration_session, errmsg)
                    return None, 1, errmsg
                else:
                    task_status = data.get_task_status()
                    if task_status is not None:
                        mixed = task_status.get_mixed()
                        if mixed is not None:
                            restore = mixed.get_restore()
                            if restore is not None:
                                migration_session.setObject('restore_task_status', restore)
                                deploy = restore.get_deploy()
                                conflict_resolve = restore.get_conflict_resolve()
                                if deploy is not None:
                                    if deploy.get_finished() is not None:
                                        migration_session.setValue('result', deploy.get_finished().get_status())
                                        migration_session.setValue('step', 'finished')
                                        migration_session.PostMigration()
                                        break
                                    if deploy.get_stopped() is not None:
                                        migration_session.setValue('result', 'stopped')
                                        migration_session.PostMigration()
                                        break
                                elif conflict_resolve is not None:
                                    if conflict_resolve.get_stopped() is not None:
                                        migration_session.setValue('result', 'stopped')
                                        migration_session.PostMigration()
                                        break
        except subproc.NonzeroExitException, x:
            e = pmmcli_exceptions.PMMUtilityException('PMMCli', x)
            self.__setRestoreFailed(migration_session, str(e))
            raise e
        except Exception, e:
            self.__setRestoreFailed(migration_session, str(e))
            raise

        return None, 0, ''

    def __setRestoreFailed(self, migration_session, result_value, restore_task_id = None):
        migration_result_filename = None
        if restore_task_id is not None:
            task_manager = getPMMTaskManager()
            restore_task = task_manager.realGetTask(restore_task_id)

            deploy_task_id = restore_task.get('deploy_task_id')
            if deploy_task_id is not None:
                deploy_task = task_manager.realGetTask(deploy_task_id)
                migration_result_filename = deploy_task.get('migration_result_filename')
                if not os.path.exists(migration_result_filename):
                    migration_result_filename = None

            if migration_result_filename is None:
                migration_result_filename = os.path.join(restore_task.get('session_path'), 'migration.result')
                if not os.path.exists(migration_result_filename):
                    migration_result_filename = None

        if migration_result_filename is None:
            message = MessageType.factory(severity = 'error', description = result_value)
            deploy = ExecutionResult.factory(status = 'error')
            deploy.add_message(message)
            failed_restore = ExecutionResultRestore.factory(status = 'error', deploy = deploy)
            migration_session.setObject('failed-restore.result', failed_restore)
            migration_result_filename = os.path.join(migration_session.getSessionPath(), 'failed-restore.result')

        finished = Finished.factory(status = 'error', log_location = migration_result_filename)
        deploy = TaskStatus.factory(finished = finished)
        restore = TaskStatusRestore.factory(status = 'error', deploy = deploy)
        migration_session.setObject('restore_task_status', restore)

    def __setRestoreTaskId(self, migration_session, restore_task_id):
        migration_session.setValue('restore_task_id', restore_task_id)
        task_manager = getPMMTaskManager()
        task_id = migration_session.getValue('migration_task_id')
        if task_id is not None:
            task = task_manager.realGetTask(task_id)
            task.set("restore_task_id", restore_task_id)
            task_manager.updateTask(task)
        else:
            _logger.warning("Migration task for migration session %s not found" % migration_session.getSessionId())


class MigrationGetTaskStatusAction(MigrationHandlerAction):
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameters = parameters
        self.__migration_session_id = None

    def validate(self):
        if len(self.__parameters) != 1:
            return None, "Parameter 'migration-session-id' is not specified"
        self.__migration_session_id = self.__parameters[0]
        # there is no format requirements for migration-session-id
        return 1, ''

    def doActivity(self):
        if self.__migration_session_id is None:
            self.__migration_session_id = self.__parameters[0]
        
        data = None
        errcode = 0
        message = None
        try:
            session = migration_handler_session.MigrationSession( self.__migration_session_id )
            
            status = TaskStatus.factory()
            
            status_mixed_object = session.getObject('status_mixed')
            if status_mixed_object is None:
                backup = self.__getBackupTaskStatus(session)
                transfer = self.__getTransferTaskStatus(session)
                restore = self.__getRestoreTaskStatus(session)
                
                status_mixed_object = TaskStatusMixed.factory( backup = backup, transfer = transfer, restore = restore )
                
                mixed_status = self.__getMixedStatus( session, backup, transfer, restore)
                if mixed_status is not None:
                    status_mixed_object.set_status(mixed_status)
                    status_mixed_object.set_log_location( self.__getMixedLog(session, backup, transfer, restore) )
                    session.setObject('status_mixed',status_mixed_object)
            
            status.mixed = status_mixed_object
            data = Data.factory()
            data.task_status = status
        except subproc.NonzeroExitException, x:
            errcode, message = self.parse_lauchpaderror( x )
        except ExpatError, x:
            errcode = 1000
            message =  u"Xml reading error: " + unicode(x)
        
        return data, errcode, message

    def __max_status(self, array):
        status = 'success'
        for item in array:
            if item == 'error':
                return item
            if status == 'success':
                if item == 'warnings':
                    status = item
        return status

    def __getMixedStatus(self, session, backup, transfer, restore):
        statuses = []

        if restore is not None:
            restore_status = restore.get_status()
            if restore_status is not None:
                statuses.append(restore_status)
                if (transfer is not None) and (transfer.get_finished() is not None):
                    transfer_status = transfer.get_finished().get_status()
                    if transfer_status is not None:
                        statuses.append(transfer_status)
                if (backup is not None) and (backup.get_finished() is not None):
                    backup_status = backup.get_finished().get_status()
                    if backup_status is not None:
                        statuses.append(backup_status)

        if len(statuses) > 0:
            return self.__max_status(statuses)

        if not osutil.is_active(session.getValue('migration_task_id')):
            return 'error'

    def __getMixedLog(self, session, backup, transfer, restore):
        backup_execution_result = None
        if backup is not None:
            backup_log_location = None
            if backup.get_finished() is not None:
                backup_log_location = backup.get_finished().get_log_location()
            backup_execution_result = self.__get_execution_result(backup_log_location)

        transfer_execution_result = None
        if transfer is not None:
            transfer_log_location = None
            if transfer.get_finished() is not None:
                transfer_log_location = transfer.get_finished().get_log_location()
            transfer_execution_result = self.__get_execution_result(transfer_log_location)

        restore_execution_result = None
        if restore is not None:
            restore_log_location = restore.get_log_location()
            restore_execution_result = self.__get_execution_result_restore(restore_log_location)

        execution_result_mixed = ExecutionResultMixed.factory( backup = backup_execution_result, transfer = transfer_execution_result, restore = restore_execution_result)
        execution_result_mixed.set_status( self.__getMixedStatus( session, backup, transfer, restore) )
        execution_result_mixed_filename = os.path.join(session.getSessionPath(), 'mixed-result.xml')
        if not os.path.isfile(execution_result_mixed_filename):
            execution_result_mixed_file = open(execution_result_mixed_filename, 'wt')
            try:
                resp_str = cStringIO.StringIO()
                resp_encoded = EncoderFile(resp_str, "utf-8")
                resp_encoded.write('<?xml version="1.0" encoding="UTF-8"?>\n')
                execution_result_mixed.export(resp_encoded, 0, name_ = 'execution-result-mixed')
                execution_result_mixed_file.write(resp_str.getvalue())
            finally:
                execution_result_mixed_file.close()
                osutil.chown(execution_result_mixed_filename, 'psaadm', 'psaadm')
        return execution_result_mixed_filename

    def __get_execution_result_restore(self, log_filename):
        if not log_filename:
            return

        execution_result = ExecutionResultRestore.factory(log_location = None)
        log_content = None
        try:
            log_file = open(log_filename,"rt")
            log_content = log_file.read()
            log_file.close()
        except:
            pass
        if log_content is not None:
            try:
                execution_result.build(minidom.parseString(log_content).childNodes[0])
            except:
                pass
        return execution_result

    def __get_execution_result(self, log_filename):
        if not log_filename:
            return

        execution_result = ExecutionResult.factory(log_location = None)
        fatal_message = None

        log_content = None
        try:
            log_file = open(log_filename,"rt")
            log_content = log_file.read()
            log_file.close()
        except:
            fatal_message = MessageType.factory( id=None, resolution=None )
            fatal_message.set_code('UtilityError')
            fatal_message.set_severity('error')
            fatal_message.set_description("Execution result file (%s) can not be opened" % log_filename)

        if log_content is not None:
            try:
                execution_result.build(minidom.parseString(log_content).childNodes[0])
            except:
                fatal_message = MessageType.factory( id=None, resolution=None )
                fatal_message.set_code('UtilityError')
                fatal_message.set_severity('error')
                fatal_message.set_description("Execution result file (%s) is not valid" % log_filename)

        if fatal_message is not None:
            execution_result.set_status('error')
            execution_result.add_message(fatal_message)
        return execution_result

    def __getBackupTaskStatus(self, session):
        status = session.getBackupFinishedStatus()
        if status is None:
            status = self.__retrieveBackupTaskStatus(session)
            if status.finished is not None:
                session.setBackupFinishedStatus(status)
        # Should remove task_id attribute from task_status.
        # Child backup task of migration task does not have task_id.
        status.set_task_id( None )
        return status

    def __getTransferTaskStatus(self, session):
        return session.getTransferStatus()

    def __retrieveBackupTaskStatus(self, session):
        agent_credentials = session.getObject('agent_credentials')
        host_credentials = agent_credentials.host_credentials
        
        service = MigrationRemoteService.getService(host_credentials.hostname, host_credentials.port, host_credentials.login, host_credentials.password, session)
        
        status = service.getStatus(session.getValue('agent_name'), session.getValue('agent_dir'))
        
        return status

    def __getRestoreTaskStatus(self, session):
        restore_task_status = session.getObject('restore_task_status')
        if restore_task_status is None and session.getValue('restore_task_id') is not None:
            cmd = subproc.CmdLine(pmm_config.pmmcli())
            cmd.arg('--get-task-status')
            cmd.arg(session.getValue('restore_task_id'))
            errcode = 0
            message = None
            data = None
            try:
                proc = cmd.spawn()
                get_task_status_result = proc.stdout.encode('utf-8')
                response = Response.factory()
                response.build(minidom.parseString(get_task_status_result).childNodes[0])
                data = response.get_data()
                if data is None:
                    _logger.warning("could not get restore task status from --get-task-status result: %s" % get_task_status_result)
                else:
                    task_status = data.get_task_status()
                    if task_status is not None:
                        mixed = task_status.get_mixed()
                        if mixed is not None:
                            restore_task_status = mixed.get_restore()
                            if restore_task_status is not None:
                                session.setObject('restore_task_status', restore_task_status)
            except subproc.NonzeroExitException, x:
                errcode = x.exitcode
                message =  u"pmmcli --get-task-status error (Error code = " + unicode(errcode) + "):"  \
                         + u"\n== STDOUT ====================\n" + x.subprocess.stdout + u"\n==============================\n" \
                         + u"== STDERR ====================\n" + x.subprocess.stderr + u"\n==============================\n"
                _logger.warning(message)
        return restore_task_status


class MigrationGetTaskLogAction(MigrationHandlerAction):
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameters = parameters
        self.__migration_session_id = None

    def validate(self):
        if len(self.__parameters) != 1:
            return None, "Parameter 'migration-session-id' is not specified"
        self.__migration_session_id = self.__parameters[0]
        # there is no format requirements for migration-session-id
        return 1, ''

    def doActivity(self):
        if self.__migration_session_id is None:
            self.__migration_session_id = self.__parameters[0]
        
        data = None
        errcode = 0
        message = None
        try:
            session = migration_handler_session.MigrationSession( self.__migration_session_id )
            
            task_log = self.__getTaskLog(session)
            
            data = Data.factory()
            data.task_log = task_log
        except subproc.NonzeroExitException, x:
            errcode, message = self.parse_lauchpaderror( x )
        except ExpatError, x:
            errcode = 1000
            message =  u"Xml reading error: " + unicode(x)
        
        return data, errcode, message

    def __getTaskLog(self, session):
        task_log_file = os.path.join(session.getSessionPath(),'migration.log')
        log_string = "Log file \'" + task_log_file + "\' does not exist"
        try:
            if os.path.isfile(task_log_file):
                task_log = open(task_log_file,"rt")
                log_string = task_log.read().decode('latin-1')
                task_log.close()
        except Exception, x:
            log_string = "Exception raised when get migration log: %s" % str(x)
            _logger.warning(log_string)
        
        return TaskLog ( log_string.encode('utf-8') )


class MigrationGetSessionsListAction(MigrationHandlerAction):
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)

    def validate(self):
        return 1, ''

    def doActivity(self):
        session_list_object = migration_handler_session.getMigrationSessionManager().getSessionListObject()
        data = Data.factory( session_list = session_list_object )
        return data, 0, ''


class MigrationGetSessionAction(MigrationHandlerAction):
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameters = parameters
        self.__migration_session_id = None

    def validate(self):
        if len(self.__parameters) != 1:
            return None, "Parameter 'migration-session-id' is not specified"
        self.__migration_session_id = self.__parameters[0]
        # there is no format requirements for migration-session-id
        return 1, ''

    def doActivity(self):
        if self.__migration_session_id is None:
            self.__migration_session_id = self.__parameters[0]
        
        session = migration_handler_session.MigrationSession( self.__migration_session_id )
        
        session_object = migration_handler_session.getMigrationSessionManager().getSessionObject(session)
        
        session_list_object = SessionList.factory()
        session_list_object.add_session( session_object )
        data = Data.factory( session_list = session_list_object )
        return data, 0, ''


class MigrationRemoveSessionDataAction(MigrationHandlerAction):
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameters = parameters
        self.__migration_session_id = None

    def validate(self):
        if len(self.__parameters) != 1:
            return None, "Parameter 'migration-session-id' is not specified"
        self.__migration_session_id = self.__parameters[0]
        # there is no format requirements for migration-session-id
        return 1, ''

    def doActivity(self):
        if self.__migration_session_id is None:
            self.__migration_session_id = self.__parameters[0]
        
        migration_session = migration_handler_session.MigrationSession(self.__migration_session_id)
        agent_credentials = migration_session.getObject('agent_credentials')
        service = MigrationRemoteService.getService(agent_credentials.get_host_credentials().get_hostname(),
                                                    agent_credentials.get_host_credentials().get_port(),
                                                    agent_credentials.get_host_credentials().get_login(),
                                                    agent_credentials.get_host_credentials().get_password(),
                                                    migration_session)
        service.removeData(migration_session.getValue('agent_name'), migration_session.getValue('agent_dir'))
        
        migration_handler_session.getMigrationSessionManager().removeSession(migration_session)
        
        return None, 0, None


class MigrationStopTaskAction(MigrationHandlerAction):
    def __init__(self, parameter_stdin, parameters):
        MigrationHandlerAction.__init__(self, parameter_stdin, parameters)
        self.__parameters = parameters
        self.__migration_session_id = None

    def validate(self):
        if len(self.__parameters) != 1:
            return None, "Parameter 'migration-session-id' is not specified"
        self.__migration_session_id = self.__parameters[0]
        # there is no format requirements for migration-session-id
        return 1, ''

    def doActivity(self):
        if self.__migration_session_id is None:
            self.__migration_session_id = self.__parameters[0]
        
        migration_session = migration_handler_session.MigrationSession(self.__migration_session_id)
        
        if migration_session.getFlag('stopped'):
            return None, 0, None
        
        migration_session.setValue('step','stopped')
        
        import osutil
        import signal
        migration_pid = migration_session.getMigrationPid()
        if migration_pid is not None:
            _logger.info('Found migration pid=' + str(migration_pid))
            if osutil.is_active(migration_pid):
                _logger.info('Try to stop migration')
                if not osutil.kill(migration_pid, signal.SIGTERM):
                    _logger.info('Unable to kill process with pid=' + str(migration_pid))
                else:
                    _logger.info('Migration pid=' + str(migration_pid) + ' is stopped')
        
        for pid in migration_session.getWorkerPids():
            _logger.info('Found migration worker pid=' + str(pid))
            if osutil.is_active(pid):
                _logger.info('Try to stop migration worker')
                if not osutil.kill(pid, signal.SIGTERM):
                    _logger.info('Unable to kill process with pid=' + str(pid))
                else:
                    _logger.info('Migration worker pid=' + str(pid) + ' is stopped')
        
        migration_session.setFlag('stopped')
        migration_session.PostMigration()
        return None, 0, None


def reset_stdin():
    if sys.stdin != sys.__stdin__:
        sys.stdin = sys.__stdin__


def safe_print(packet):
    try:
        sys.stdout.write(packet)
    except IOError:
        pass


def validate_path(path):
    if not os.path.isabs(path):
        return (False, "Session path must be absolute: " +  path + "\n")
    
    if os.path.exists(path) and not os.path.isdir(path):
        return (False, "Path " + path + " exists, but not a directory.\n")
    if not os.path.exists(path):
        try:
            dirutil.mkdirs(path, 0750)
        except UnicodeError, e:
            return (False, "Invalid character in session directory\n")
        except OSError, e:
            return (False, "Unable to create directory " + path + ": " + e.strerror + "\n")
    return (True, None)


def migration_get_scout_info(parameters):
    migration_task_description = sys.stdin.read()
    return MigrationHandlerActionRunner(MigrationGetScoutInfoAction, migration_task_description, None).doActivity()


def migration_get_objects_list(parameters):
    migration_session_id = parameters
    agent_to_use = sys.stdin.read()
    return MigrationHandlerActionRunner(MigrationGetObjectsListAction, agent_to_use, migration_session_id).doActivity()


def migration_set_ip_mapping(parameters):
    # <parameter1> is migration_session_id
    migration_session_id = parameters
    ip_mapping = sys.stdin.read()
    return MigrationHandlerActionRunner(MigrationSetIPMappingAction, ip_mapping, migration_session_id).doActivity()


def migration_get_ip_mapping(parameters):
    # <parameter1> is migration_session_id
    migration_session_id = parameters
    return MigrationHandlerActionRunner(MigrationGetIPMappingAction, None, migration_session_id).doActivity()


def migration_get_ip_for_mapping(parameters):
    # <parameter1> is migration_session_id
    migration_session_id = parameters
    return MigrationHandlerActionRunner(MigrationGetIPForMappingAction, None, migration_session_id).doActivity()


def migration_set_db_mapping(parameters):
    # <parameter1> is migration_session_id
    migration_session_id = parameters
    db_mapping = sys.stdin.read()
    return MigrationHandlerActionRunner(MigrationSetDBMappingAction, db_mapping, migration_session_id).doActivity()


def migration_get_db_mapping(parameters):
    # <parameter1> is migration_session_id
    migration_session_id = parameters
    return MigrationHandlerActionRunner(MigrationGetDBMappingAction, None, migration_session_id).doActivity()


def migration_set_owner_mapping(parameters):
    # <parameter1> is migration_session_id
    migration_session_id = parameters
    owner_mapping = sys.stdin.read()
    return MigrationHandlerActionRunner(MigrationSetOwnerMappingAction, owner_mapping, migration_session_id).doActivity()


def migration_get_owner_mapping(parameters):
    # <parameter1> is migration_session_id
    migration_session_id = parameters
    return MigrationHandlerActionRunner(MigrationGetOwnerMappingAction, None, migration_session_id).doActivity()


def migration_get_dst_temporary_directory(parameters):
    # <parameter1> is migration_session_id
    migration_session_id = parameters
    return MigrationHandlerActionRunner(MigrationGetDstTemporaryDirectoryAction, None, migration_session_id).doActivity()


def migration_set_policy(parameters):
    # <parameter1> is migration_session_id
    migration_session_id = parameters
    conflict_resolution_rules = sys.stdin.read()
    return MigrationHandlerActionRunner(MigrationSetRestorePolicyAction, conflict_resolution_rules, migration_session_id).doActivity()


def migration_get_policy(parameters):
    # <parameter1> is migration_session_id
    migration_session_id = parameters
    return MigrationHandlerActionRunner(MigrationGetRestorePolicyAction, None, migration_session_id).doActivity()


def migration_get_selected_objects(parameters):
    # <parameter1> is migration_session_id
    migration_session_id = parameters
    return MigrationHandlerActionRunner(MigrationGetSelectedObjectsAction, None, migration_session_id).doActivity()


def migration_get_session_objects_list(parameters):
    # <parameter1> is migration_session_id
    migration_session_id = parameters
    return MigrationHandlerActionRunner(MigrationGetSessionObjectsListAction, None, migration_session_id).doActivity()


def migration_get_agent_credentials(parameters):
    # <parameter1> is migration_session_id
    migration_session_id = parameters
    return MigrationHandlerActionRunner(MigrationGetAgentCredentialsAction, None, migration_session_id).doActivity()


def migration_get_ip_for_mapping_objects(parameters):
    # <parameter1> is migration_session_id
    migration_session_id = parameters
    selected_objects = sys.stdin.read()
    return MigrationHandlerActionRunner(MigrationGetIPForMappingObjectsAction, selected_objects, migration_session_id).doActivity()


def migration_task_start(parameters):
    # <parameter1> is migration_session_id
    migration_session_id = parameters
    return MigrationHandlerActionRunner(MigrationTaskStartAction, None, migration_session_id).doActivity()


def migration_start(parameters):
    # <parameter1> is migration_session_id
    migration_session_id = parameters
    return MigrationHandlerActionRunner(MigrationStartAction, None, migration_session_id).doActivity()


def migration_get_task_status(parameters):
    # <parameter1> is migration_session_id
    migration_session_id = parameters
    return MigrationHandlerActionRunner(MigrationGetTaskStatusAction, None, migration_session_id).doActivity()


def migration_get_task_log(parameters):
    # <parameter1> is migration_session_id
    migration_session_id = parameters
    return MigrationHandlerActionRunner(MigrationGetTaskLogAction, None, migration_session_id).doActivity()


def migration_get_sessions_list(parameters):
    return MigrationHandlerActionRunner(MigrationGetSessionsListAction, None, None).doActivity()


def migration_get_session(parameters):
    # <parameter1> is migration_session_id
    migration_session_id = parameters
    return MigrationHandlerActionRunner(MigrationGetSessionAction, None, migration_session_id).doActivity()


def migration_remove_session_data(parameters):
    # <parameter1> is migration_session_id
    migration_session_id = parameters
    return MigrationHandlerActionRunner(MigrationRemoveSessionDataAction, None, migration_session_id).doActivity()


def migration_stop_task(parameters):
    # <parameter1> is migration_session_id
    migration_session_id = parameters
    return MigrationHandlerActionRunner(MigrationStopTaskAction, None, migration_session_id).doActivity()


def convertToXmlString(response, name):
    resp_str = cStringIO.StringIO()
    resp_encoded = EncoderFile(resp_str, "utf-8")
    resp_encoded.write('<?xml version="1.0" encoding="UTF-8"?>\n')
    response.export(resp_encoded, 0, name_ = name)
    packet = resp_str.getvalue()
    return packet


def main():
    actions = {"migration-get-scout-info": migration_get_scout_info,
               "migration-get-objects-list": migration_get_objects_list,
               "migration-set-ip-mapping": migration_set_ip_mapping,
               "migration-get-ip-mapping": migration_get_ip_mapping,
               "migration-set-db-mapping": migration_set_db_mapping,
               "migration-get-db-mapping": migration_get_db_mapping,
               "migration-set-policy": migration_set_policy,
               "migration-get-policy": migration_get_policy,
               "migration-set-owner-mapping": migration_set_owner_mapping,
               "migration-get-owner-mapping": migration_get_owner_mapping,
               "migration-get-dst-temporary-directory": migration_get_dst_temporary_directory,
               "migration-get-ip-for-mapping": migration_get_ip_for_mapping,
               "migration-get-ip-for-mapping-objects": migration_get_ip_for_mapping_objects,
               "migration-get-selected-objects": migration_get_selected_objects,
               "migration-get-session-objects-list": migration_get_session_objects_list,
               "migration-get-agent-credentials": migration_get_agent_credentials,
               "migration-task-start": migration_task_start,
               "migration-start": migration_start,
               "migration-get-task-status":  migration_get_task_status,
               "migration-get-task-log":  migration_get_task_log,
               "migration-get-sessions-list":  migration_get_sessions_list,
               "migration-get-session":  migration_get_session,
               "migration-remove-session-data":  migration_remove_session_data,
               "migration-stop-task":  migration_stop_task}
    
    if len(sys.argv) < 2:
        response = Response( errcode = 1, errmsg = "action not specified")
        packet = convertToXmlString(response,'response')
        _logger.info("Outgoing packet:\n" + packet)
        safe_print(packet)
    
    if not sys.argv[1][2:] in actions:
        response = Response( errcode = 1, errmsg = "action '%s' is not in list of allowed actions" % sys.argv[1])
        packet = convertToXmlString(response,'response')
        _logger.info("Outgoing packet:\n" + packet)
        safe_print(packet)
        print packet
        sys.exit(2)
    
    migration_handler_log_dir = pmm_config.pmm_logs_directory()
    parameters = sys.argv[2:]
    validate_path(migration_handler_log_dir)
    log.initPidLog("migration_handler", migration_handler_log_dir, 1)
    
    tempstdout = cStringIO.StringIO()
    tempstderr = cStringIO.StringIO()
    
    try:
        try:
            error_message = ""
            sys.stdout = tempstdout
            sys.stderr = tempstderr
            try:
                data_action_response, errcode_response, error_message = actions.get(sys.argv[1][2:])(parameters)
                if not errcode_response:
                    errcode_response = 0
            except MigrationHandlerActionParamException, ex:
                errcode_response = 1
                data_action_response = None
                error_message = "Invalid input parameters in '" + sys.argv[1] + "' command:\n" + ex.get_message()
            if not error_message:
                error_message = ""
            if tempstdout.getvalue():
                error_message = error_message + "STDOUT:\n-----------------------\n" + tempstdout.getvalue() + "\n" 
            if tempstderr.getvalue():
                error_message = error_message + "STDERR:\n-----------------------\n" + tempstderr.getvalue() + "\n"
        finally:
            sys.stdout = sys.__stdout__
            sys.stderr = sys.__stderr__
            reset_stdin()
        response = Response( errcode = errcode_response, data = data_action_response, errmsg = error_message)
        packet = convertToXmlString(response,'response')
        _logger.info("Outgoing packet:\n" + packet)
        safe_print(packet)
    except pmmcli_exceptions.PMMException, e:
        response = Response( errcode = e.getCode(), errmsg = unicode(e))
        packet = convertToXmlString(response,'response')
        _logger.info("Outgoing packet:\n" + packet)
        safe_print(packet)
    except migration_handler_session.MigrationSessionException, e:
        response = Response( errcode = 3, errmsg = "Migration session '%s' raised exception: %s" % (e.get_migration_session_id(), str(e)))
        packet = convertToXmlString(response,'response')
        _logger.info("Outgoing packet:\n" + packet)
        safe_print(packet)
    except Exception, e:
        _logger.critical("Runtime error in migration handler: \n" + str(e.__class__) + " " + str(e) + "\n" +  stacktrace.stacktrace())
        response = Response( errcode = 1001, errmsg = "Runtime error in migration handler: \n" + str(e.__class__) + " " + str(e) + "\n" +  stacktrace.stacktrace())
        packet = convertToXmlString(response,'response')
        _logger.info("Outgoing packet:\n" + packet)
        safe_print(packet)
        sys.exit(1)
    except:
        _logger.critical("Unhandled exception in migration handler: \n" +  stacktrace.stacktrace())
        response = Response( errcode = 1002, errmsg = "Unhandled exception in migration handler: \n" +  stacktrace.stacktrace())
        packet = convertToXmlString(response,'response')
        _logger.info("Outgoing packet:\n" + packet)
        safe_print(packet)
        sys.exit(1)


if __name__ == '__main__':
    main()

