import logging
import os

import parallels.pmm_unix_migrator
from parallels.common.utils.unix_utils import format_command
from parallels.common.utils import migrator_utils
from parallels.common import run_and_check_local_command
from parallels.common import MigrationError
from parallels.common.utils.pmm.agent import PmmMigrationAgentBase, DumpAll


class UnixPmmMigrationAgent(PmmMigrationAgentBase):
	""" A wrapper for legacy migration agents."""
	logger = logging.getLogger(__name__)

	def __init__(self, global_context, server, migrator_pmm_dir, settings, cli_options):
		"""Object constructor.

		Arguments:
			global_context: registry for access to global migration storage
			server: an instance of class 'SourceServer'
			migrator_pmm_dir: a directory with migration agent app code
			settings: a config.ini section with source server settings
			cli_options: migration agent CLI options
		"""

		self.common_pmm_dir = migrator_utils.get_package_extras_file_path(
			parallels.pmm_unix_migrator, 'pmm'
		)
		self.migrator_pmm_dir = migrator_pmm_dir
		self.settings = settings
		self.cli_options = cli_options

		super(UnixPmmMigrationAgent, self).__init__(global_context, server)

	def _run(self, dump_xml_filename, dump_log_filename, selection=DumpAll()):
		"""Run migration agent on a source panel and download the results.
		
		Execute the agent which puts results into XML dump file.
		Download dump XML file and log file to specified locations:
		- dump_xml_filename - local filename to put dump XML to
		- dump_log_filename - local filename to put dump log to

		Raise MigrationError in case of any failure.
		"""
		self._run_agent_script(
			script_filename=self.settings.agent_script_name,
			local_data_filename=dump_xml_filename, remote_data_filename='dump.xml',
			local_log_filename=dump_log_filename, remote_log_filename='dump.log',
			log_step_info_msg=u"Create source panel dump XML", 
			log_execute_msg=u"Execute PMM agent's create dump XML script",
			log_download_data_file_msg=u"Download dump log to '%s'",
			log_download_log_file_msg=u"Download dump XML to '%s'",
			error_run_script_msg=u"Error creating migration dump: %s",
			error_download_log_msg=u"Error downloading dump log file: %s",
		)

	def check(self):
		"""Execute pre-migration checks on a source panel.

		Raise MigrationError in case of any failure.

		Returns:
			Path to generated report file.
		"""
		self._run_agent_script(
			script_filename='PreMigration.pl',
			local_data_filename=self.check_report_file,
			remote_data_filename='pre-migration.xml',
			local_log_filename=self.check_log_file,
			remote_log_filename='pre-migration.log',
			log_step_info_msg=u"Run pre-migration checks", 
			log_execute_msg=u"Execute PMM agent's pre-migration script",
			log_download_data_file_msg=u"Download pre-migration log to '%s'",
			log_download_log_file_msg=u"Download pre-migration report XML to '%s'",
			error_run_script_msg=u"Error performing pre-migration checks",
			error_download_log_msg=u"Error downloading pre-migration log file: %s"
		)

		return self.check_report_file

	def _run_agent_script(
		self,
		script_filename,
		local_data_filename, remote_data_filename,
		local_log_filename, remote_log_filename,
		log_step_info_msg, log_execute_msg,
		log_download_data_file_msg,
		log_download_log_file_msg,
		error_run_script_msg,
		error_download_log_msg
	):
		self.logger.info(log_step_info_msg)

		try:
			with self._source_server.runner() as runner:
				self.logger.debug(log_execute_msg)
				try:
					runner.sh(
						ur'cd {path}; perl {script} %s > {log} 2>&1' % (
							self.cli_options
						),
						{
							'path':	self.settings.agent_home,
							'script': script_filename,
							'log': remote_log_filename,
						}
					)
					self.logger.debug(
						log_download_data_file_msg, local_data_filename
					)
					runner.get_file(
						os.path.join(
							self.settings.agent_home, 
							remote_data_filename
						),
						local_data_filename
					)
				finally:  # always try to download log, even if script failed
					try:
						self.logger.debug(
							log_download_log_file_msg, local_log_filename
						)
						runner.get_file( 
							os.path.join(
								self.settings.agent_home, 
								remote_log_filename
							),
							local_log_filename
						)
					except Exception as e:
						self.logger.debug(
							u'Exception', exc_info=True
						)
						self.logger.error(error_download_log_msg % e)

		except Exception as e:
			self.logger.debug(
				u'Exception', exc_info=True
			)
			raise MigrationError(error_run_script_msg % e)

	def _deploy(self):
		"""Deploy migration agent files to a source panel server."""

		self.logger.info(u"Deploy migration agent to '%s'" % self._source_server.ip())

		def local_sh(command):
			return run_and_check_local_command(['/bin/sh', '-c', command])

		with self._source_server.runner() as runner:
			runner.mkdir(self.settings.agent_home)

		key_pathname = self.global_context.ssh_key_pool.get(
			self._source_server, self.global_context.migrator_server
		).key_pathname
		local_sh(
			format_command(
				"rsync -a  "
				"-e 'ssh -i {ssh_key} -o StrictHostKeyChecking=no -o GSSAPIAuthentication=no' "
				"--delete {local_dir}/ {remote_ip}:{remote_dir}",
				ssh_key=key_pathname,
				local_dir=self.common_pmm_dir,
				remote_ip=self._source_server.ip(),
				remote_dir=self.settings.agent_home
			)
		)
		local_sh(
			format_command(
				"rsync -a "
				"-e 'ssh -i {ssh_key} -o StrictHostKeyChecking=no -o GSSAPIAuthentication=no' "
				"{local_dir}/ {remote_ip}:{remote_dir}",
				ssh_key=key_pathname,
				local_dir=self.migrator_pmm_dir,
				remote_ip=self._source_server.ip(),
				remote_dir=self.settings.agent_home
			)
		)
