from parallels.ppa.source.hsphere import messages
import logging
import string
import random
import pipes

from parallels.core.utils import unix_utils
from parallels.core.utils.ssh_utils import ExecutionError


class HsphereRunner:
	"""Run commands on H-Sphere service nodes through H-Sphere main node

	H-Sphere puts 'cpanel' user's SSH public key to all remote nodes to have an access to them.
	As it is not guaranteed that we have a direct SSH access to all H-Sphere remote nodes
	from a box with migrator tool, we:
	1) go to H-Sphere CP server by SSH
	2) "login" as 'cpanel' user
	3) use its SSH key to access all remote nodes
	"""
	logger = logging.getLogger(__name__)
	
	def __init__(self, ssh_runner, ip):
		self.ssh_runner = ssh_runner
		self.ip = ip
	
	def sh(self, cmd_str, args=None, stdin_content=None):
		formated_cmd = self._format_command(cmd_str, args)
		if stdin_content is not None:
			return self.ssh_runner.sh(
				"""su - cpanel -c "ssh root@%s '%s'" """ % (self.ip, formated_cmd), None, stdin_content
			)
		self.logger.debug(messages.EXECUTE_REMOTE_COMMAND_AS_MULTILINE_COMMAND)
		return self.ssh_runner.sh(
			"su - cpanel", None, u"ssh -o StrictHostKeyChecking=no -o GSSAPIAuthentication=no root@%s %s" % (
				self.ip, pipes.quote(formated_cmd)
			)
		)
	
	def sh_unchecked(self, cmd_str, args=None, stdin_content=None):
		formated_cmd = self._format_command(cmd_str, args)
		if stdin_content is not None:
			return self.ssh_runner.sh_unchecked(
				"""su - cpanel -c "ssh -o StrictHostKeyChecking=no -o GSSAPIAuthentication=no root@%s '%s'" """ % (
					self.ip, formated_cmd
				), None, stdin_content
			)
		self.logger.debug(messages.EXECUTE_REMOTE_COMMAND_AS_MULTILINE_COMMAND)
		return self.ssh_runner.sh_unchecked(
			"su - cpanel", None, u"ssh -o StrictHostKeyChecking=no -o GSSAPIAuthentication=no root@%s %s" % (
				self.ip, pipes.quote(formated_cmd)
			)
		)
		
	def _format_command(self, cmd_str, args):
		""" Due to such construction does not support multiline cmd and stdin
			we switch type of command execution for multiline cmd and multiline stdin  
		"""
		if args is not None:
			cmd_str = unix_utils.format_command(cmd_str, **args)
		self.logger.debug(messages.DEBUG_EXECUTE_COMMAND_ON_HSPHERE, cmd_str, self.ip)
		return cmd_str
		
	def run(self, cmd, args=[], stdin_content=None):
		exit_status, stdout_content, stderr_content = self.run_unchecked(cmd, args, stdin_content)
		if exit_status != 0:
			raise ExecutionError(cmd, exit_status, stdout_content, stderr_content)
		else:
			return stdout_content
	
	def run_unchecked(self, cmd, args=[], stdin_content=None):
		cmd_str = " ".join([
			pipes.quote(arg) for arg in [cmd] + args 
		])
		self.logger.debug(messages.DEBUG_EXECUTE_COMMAND_ON_HSPHERE, cmd, self.ip)
		return self.ssh_runner.sh_unchecked(
			"su - cpanel", None,
			u"ssh -o StrictHostKeyChecking=no -o GSSAPIAuthentication=no root@%s '%s'" % (self.ip, cmd_str)
		)

	def run_script(self, script, stdin_content=None):
		def rand_str(size=6, chars=string.ascii_uppercase + string.digits):
			return ''.join(random.choice(chars) for _ in range(size))
		
		script_temp_filename = '/tmp/temp_script.' + rand_str(5)
		stdin_temp_filename = '/tmp/temp_stdin.' + rand_str(5)
		
		self.logger.debug(messages.LOAD_SCRIPT_CONTENT_HSPHERE_SERVER, self.ip)
		self.sh(u"cat > %s" % script_temp_filename, None, stdin_content=script)
		
		if stdin_content is not None:
			self.logger.debug(messages.LOAD_STDIN_CONTENT_TO_HSPHERE_SERVER, self.ip)
			self.sh(u"cat > %s" % stdin_temp_filename, stdin_content=stdin_content)

		self.logger.debug(messages.DEBUG_EXECUTE_SCRIPT_ON_HSPHERE, self.ip)
		if stdin_content is not None:
			ret = self.sh(u"cat %s | /bin/sh %s" % (stdin_temp_filename, script_temp_filename))
		else:
			ret = self.sh(u"/bin/sh %s" % script_temp_filename)

		self.logger.debug(messages.REMOVE_TEMP_FILES_FROM_HSPHERE_SERVER, self.ip)
		self.sh(u"rm -rf %s" % script_temp_filename)
		if stdin_content is not None:
			self.sh(u"rm -rf %s" % stdin_temp_filename)
		
		return ret
	
	def remove_file(self, filename):
		return self.run('/bin/rm', [filename])
