import logging
import os
import posixpath

import parallels.pmm_unix_migrator
from parallels.common.utils.ssh_utils import public_key_ssh_access
from parallels.common.utils.unix_utils import format_command
from parallels.common import run_and_check_local_command
from parallels.common import MigrationError

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

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

		Arguments:
			migrator_pmm_dir: a directory with migration agent app code
			server: an instance of class 'SourceServer'
			settings: a config.ini section with source server settings
			cli_options: migration agent CLI options
		"""
			
		self.server = server
		self.settings = settings
		self.migrator_pmm_dir = migrator_pmm_dir
		self.common_pmm_dir = posixpath.join(
			self._get_common_root_path(), 'extras', 'pmm'
		)
		self.cli_options = cli_options

		self._deploy()

	def run_agent(self, dump_xml_filename, dump_log_filename):
		"""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 run_pre_migration(
		self, pre_migration_report_xml_filename, pre_migration_log_filename
	):
		"""Run pre-migration check script on a source panel.
		
		Execute the pre-migration check script which puts results 
		into XML report file.
		Download report XML file and log file to specified locations:
		- pre_migration_report_xml_filename - local filename 
		to put report XML to
		- pre_migration_log_filename - local filename 
		to put pre-migration log to

		Raise MigrationError in case of any failure.
		"""
		self._run_agent_script(
			script_filename='PreMigration.pl',
			local_data_filename=pre_migration_report_xml_filename, 
			remote_data_filename='pre-migration.xml',
			local_log_filename=pre_migration_log_filename, 
			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"
		)

	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.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.server.ip())

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

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

			with public_key_ssh_access(local_sh, runner.sh) \
					as key_pathname:
				local_sh(
					format_command(
						"rsync -a  "
						"-e 'ssh -i {ssh_key} -o StrictHostKeyChecking=no' "
						"--delete {local_dir}/ {remote_ip}:{remote_dir}",
						ssh_key=key_pathname,
						local_dir=self.common_pmm_dir,
						remote_ip=self.server.ip(),
						remote_dir=self.settings.agent_home
					)
				)
				local_sh(
					format_command(
						"rsync -a "
						"-e 'ssh -i {ssh_key} -o StrictHostKeyChecking=no' "
						"{local_dir}/ {remote_ip}:{remote_dir}",
						ssh_key=key_pathname,
						local_dir=self.migrator_pmm_dir,
						remote_ip=self.server.ip(),
						remote_dir=self.settings.agent_home
					)
				)

	def _get_common_root_path(self):
		# Get path to package root directory.
		dirs = [p for p in parallels.pmm_unix_migrator.__path__]
		assert all(d == dirs[0] for d in dirs)
		return dirs[0]
