from parallels.common.actions.base.subscription_action import SubscriptionAction
from parallels.plesk_api.core import PleskError
from parallels.plesk_api.operator import AliasOperator, DnsOperator
from parallels.plesks_migrator import messages


class AddSourceToTargetDNSAXFR(SubscriptionAction):
	def get_description(self):
		return messages.ACTION_ADD_SOURCE_SERVER_TO_TARGET_DNS_AXFR_DESCRIPTION

	def get_failure_message(self, global_context, subscription):
		"""
		:type global_context: parallels.common.global_context.GlobalMigrationContext
		:type subscription: parallels.common.migrated_subscription.MigratedSubscription
		"""
		return messages.ACTION_ADD_SOURCE_SERVER_TO_TARGET_DNS_AXFR_FAILURE

	def filter_subscription(self, global_context, subscription):
		"""Check whether we should run this action or not. By default True - action should be executed.

		:type global_context: parallels.common.global_context.GlobalMigrationContext
		:type subscription: parallels.common.migrated_subscription.MigratedSubscription
		:rtype: bool
		"""
		return subscription.is_windows

	def run(self, global_context, subscription):
		"""
		:type global_context: parallels.common.global_context.GlobalMigrationContext
		:type subscription: parallels.common.migrated_subscription.MigratedSubscription
		"""
		cli_runner = global_context.conn.target.plesk_cli_runner
		plesk_api = subscription.panel_target_server.plesk_api()

		subscription_dns_ips = set(subscription.source_dns_ips)
		# additionally to source DNS IPs, add all IPs of the source panel server
		# because zone transfer requests may come from these IPs, not IP the DNS server is listening on
		subscription_dns_ips.update([
			ip.ip_address for ip in subscription.management_source_server.get_all_ips(global_context)
		])

		for dns_ip in subscription_dns_ips:
			for domain in subscription.converted_backup.iter_domains():
				if domain.dns_zone is not None and domain.dns_zone.enabled:
					try:
						cli_runner.run('dns', ['--add', domain.name.encode('idna'), '-axfr', dns_ip])
					except Exception as e:
						if 'DNS record already exists' in unicode(e):
							# if DNS record already exists (for example, was added
							# during previous set-dns-forwarding command execution) then
							# there no need to add it again, just ignore the exception with such message
							pass
						else:
							# all the other exceptions should be handled
							raise

		# we have to handle domain aliases in a special way with 2 API requests (get alias ID, then add record)
		# because Plesk 12 does not allow to create AXFR records for aliases with single CLI command
		for alias in subscription.converted_backup.iter_aliases():
			alias_id = plesk_api.send(
				AliasOperator.Get(
					filter=AliasOperator.FilterByName([alias.name]),
				)
			)[0].data[0]

			for dns_ip in subscription_dns_ips:
				try:
					plesk_api.send(
						DnsOperator.AddRec(
							target=DnsOperator.AddRec.TargetSiteAliasId(id=alias_id),
							src='',
							dst=dns_ip,
							opt=None,
							rec_type='AXFR'
						)
					)[0].check()
				except PleskError as e:
					if e.code == 1007:
						# Ignore situation when IP address already exists.
						pass
					else:
						raise