from parallels.core import messages
import logging
import itertools

from parallels.core.actions.base.subscription_backup_action import SubscriptionBackupAction
from parallels.core.actions.utils.logging_properties \
	import LoggingProperties
from parallels.core.target_panels import TargetPanels
from parallels.core.utils.common.ip import is_ipv4, is_ipv6


logger = logging.getLogger(__name__)


class ChangeSubscriptionIPs(SubscriptionBackupAction):
	"""Change IPs according to subscription's hosting addresses"""

	def get_description(self):
		return "Change IP addresses in backup"

	def get_failure_message(self, global_context, subscription):
		"""
		:type global_context: parallels.core.global_context.GlobalMigrationContext
		:type subscription: parallels.core.migrated_subscription.MigratedSubscription
		"""
		return messages.FAILED_CHANGE_IPS_FOR_SUBSCRIPTION_S % subscription.name

	def get_logging_properties(self):
		return LoggingProperties(info_log=False)

	def _run_subscription_backup(
		self, global_context, subscription, subscription_backup
	):
		"""
		:type global_context: parallels.core.global_context.GlobalMigrationContext
		:type subscription: parallels.core.migrated_subscription.MigratedSubscription
		"""
		all_target_ips = subscription.panel_target_server.get_all_ips(global_context)
		ip_address_to_type = {ip.ip_address: ip.ip_type for ip in all_target_ips}

		# Workaround for Plesk bug 130416:
		# When subscription in PPA has no web IPs (has only mail hosting),
		# pleskrestore still needs an IP to call Plesk CLI utilities, and
		# tries to figure it out from provided backup; it fails, picking
		# one of source server's IPs, with which subscription can't be
		# restored. Here we pick a suitable IP for pleskrestore.
		target_ip = global_context.conn.target.main_node_ip
		if subscription_backup.hosting_type != 'none':
			(v4, v6) = (
				subscription.target_public_web_ipv4, 
				subscription.target_public_web_ipv6
			)
		elif global_context.target_panel == TargetPanels.PPA:
			# For PPA, take management node IPs. Can not use mail IPs as mail could be located on remote server
			if is_ipv4(target_ip):
				(v4, v6) = (target_ip, None)
			elif is_ipv6(target_ip):
				(v4, v6) = (None, target_ip)
			else:
				logger.warning(
					messages.CANNOT_FIND_TARGET_IP_FOR_SUBSTITUTION % subscription.name
				)
		else:
			# For Plesk, take mail IP addresses, they should be the same as web IPs
			v4, v6 = subscription.target_public_mail_ipv4, subscription.target_public_mail_ipv6

			if v4 is None and v6 is None:
				# If mail IPs are absent too - fallback to IP speicified in config.ini
				if is_ipv4(target_ip):
					(v4, v6) = (target_ip, None)
				elif is_ipv6(target_ip):
					(v4, v6) = (None, target_ip)

			
		subscription_backup.change_web_ips([
			(v4, ip_address_to_type.get(v4, 'shared')), 
			(v6, ip_address_to_type.get(v6, 'shared'))
		])
		logger.debug(u"Subscription '%s' IP is set to %s." % (subscription.name, v4))

		for domain in itertools.chain(
			[subscription_backup], 
			subscription_backup.iter_addon_domains()
		):
			for mail_object in [domain.mailsystem, domain.maillists]:
				if mail_object is not None:
					mail_ipv4 = subscription.target_public_mail_ipv4
					mail_ipv6 = subscription.target_public_mail_ipv6
					mail_object.change_ips([
						(mail_ipv4, ip_address_to_type.get(mail_ipv4, 'shared')),
						(mail_ipv6, ip_address_to_type.get(mail_ipv6, 'shared'))
					])
